From 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d Mon Sep 17 00:00:00 2001 From: Jef Date: Tue, 24 Sep 2024 14:54:57 +0200 Subject: Initial community commit --- .../Library/ml_downloads/DownloadsDialog.cpp | 1657 ++++++++++++++++++++ 1 file changed, 1657 insertions(+) create mode 100644 Src/Plugins/Library/ml_downloads/DownloadsDialog.cpp (limited to 'Src/Plugins/Library/ml_downloads/DownloadsDialog.cpp') diff --git a/Src/Plugins/Library/ml_downloads/DownloadsDialog.cpp b/Src/Plugins/Library/ml_downloads/DownloadsDialog.cpp new file mode 100644 index 00000000..adb2b008 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/DownloadsDialog.cpp @@ -0,0 +1,1657 @@ +#include "main.h" +#include "api__ml_downloads.h" +#include "RFCDate.h" +#include "Downloaded.h" +#include "DownloadStatus.h" +#include "Defaults.h" +#include "../nu/listview.h" +#include "..\..\General\gen_ml/ml_ipc.h" +#include "..\..\General\gen_ml/menu.h" +#include +#include "../nu/menushortcuts.h" +#include +#include +#include +#include +#include + +HWND downloads_window = 0; +extern int downloads_treeItem; +extern int no_auto_hide; +int groupBtn = 1, enqueuedef = 0, customAllowed = 0; +HMENU g_context_menus2 = NULL; +static viewButtons view; + +#ifndef HDF_SORTUP +#define HDF_SORTUP 0x0400 +#define HDF_SORTDOWN 0x0200 +#endif // !HDF_SORTUP + +using namespace Nullsoft::Utility; + +enum +{ + COL_TITLE = 0, + COL_PROGRESS, + COL_DATE, + COL_SOURCE, + COL_SIZE, + COL_PATH, + NUM_COLUMNS, +}; + +int downloadsSourceWidth = DOWNLOADSSOURCEWIDTHDEFAULT; +int downloadsTitleWidth = DOWNLOADSTITLEWIDTHDEFAULT; +int downloadsProgressWidth = DOWNLOADSPROGRESSWIDTHDEFAULT; +int downloadsDateWidth = DOWNLOADSDATEWIDTHDEFAULTS; +int downloadsSizeWidth = DOWNLOADSSIZEWIDTHDEFAULTS; +int downloadsPathWidth = DOWNLOADSPATHWIDTHDEFAULTS; + + +W_ListView downloadList; +int downloadsItemSort = 2; // -1 means no sort active +bool downloadsSortAscending = false; + +enum +{ + DOWNLOADSDIALOG_TIMER_UPDATESTATUSBAR = 0, +}; + +class DownloadListItem +{ +public: + DownloadedFile *f = NULL; + DownloadToken token = NULL; + wchar_t *source = 0; + wchar_t *title = 0; + wchar_t *path = 0; + wchar_t status[ 20 ] = { 0 }; + + DownloadListItem( DownloadedFile *fi ) + { + f = new DownloadedFile( *fi ); + ZeroMemory( status, sizeof( status ) ); + } + + DownloadListItem( DownloadToken p_token, const wchar_t *p_source, const wchar_t *p_title, const wchar_t *p_path, size_t p_downloaded, size_t p_maxSize ) : token( p_token ) + { + if ( p_maxSize ) + StringCchPrintf( status, 20, WASABI_API_LNGSTRINGW( IDS_DOWNLOADING_PERCENT ), (int)( p_downloaded / ( p_maxSize / 100 ) ) ); + else + { + if ( WAC_API_DOWNLOADMANAGER->IsPending( p_token ) ) + WASABI_API_LNGSTRINGW_BUF( IDS_DOWNLOAD_PENDING, status, 20 ); + else + WASABI_API_LNGSTRINGW_BUF( IDS_DOWNLOADING, status, 20 ); + } + + source = p_source ? _wcsdup( p_source ) : NULL; + title = p_title ? _wcsdup( p_title ) : NULL; + path = p_path ? _wcsdup( p_path ) : NULL; + } + + ~DownloadListItem() + { + clean(); + + if ( f ) + delete f; + } + + void clean() + { + if ( source ) + { + free( source ); + source = 0; + } + + if ( title ) + { + free( title ); + title = 0; + } + + if ( path ) + { + free( path ); + path = 0; + } + } +}; + +static std::vector listContents; + +bool GetDownload( int &download ) +{ + download = ListView_GetNextItem( downloadList.getwnd(), download, LVNI_ALL | LVNI_SELECTED ); + if ( download == -1 ) + return false; + else + return true; +} + +void Downloads_Play( bool enqueue = false ) +{ + int download = -1; + AutoLock lock( downloadedFiles ); + while ( GetDownload( download ) ) + { + if ( !enqueue ) + { + if ( listContents[ download ]->f ) + mediaLibrary.PlayFile( listContents[ download ]->f->path ); + else if ( listContents[ download ]->path ) + mediaLibrary.PlayFile( listContents[ download ]->path ); + + enqueue = true; + } + else + { + if ( listContents[ download ]->f ) + mediaLibrary.EnqueueFile( listContents[ download ]->f->path ); + else if ( listContents[ download ]->path ) + mediaLibrary.EnqueueFile( listContents[ download ]->path ); + } + } +} + +void DownloadsUpdated( const DownloadStatus::Status &s, DownloadToken token ) +{ + listContents.push_back( new DownloadListItem( token, s.source, s.title, s.path, s.downloaded, s.maxSize ) ); + + downloadList.SetVirtualCountAsync( (int)listContents.size() ); +} + +void DownloadsUpdated( DownloadToken token, const DownloadedFile *f ) +{ + for ( DownloadListItem *l_content : listContents ) + { + if ( l_content->token == token ) + { + l_content->token = 0; + if ( f ) + { + l_content->f = new DownloadedFile( *f ); + + l_content->clean(); + } + else + lstrcpyn( l_content->status, L"Error", 20 ); + + break; + } + } + + PostMessage( downloadList.getwnd(), LVM_REDRAWITEMS, 0, listContents.size() ); +} + +void DownloadsUpdated() +{ + for ( DownloadListItem *l_content : listContents ) + delete l_content; + + listContents.clear(); + + for ( DownloadedFile &l_download : downloadedFiles.downloadList ) + listContents.push_back( new DownloadListItem( &l_download ) ); + + { + AutoLock lock( downloadStatus.statusLock ); + for ( DownloadStatus::Downloads::iterator itr = downloadStatus.downloads.begin(); itr != downloadStatus.downloads.end(); itr++ ) + { + listContents.push_back( new DownloadListItem( itr->first, itr->second.source, itr->second.title, itr->second.path, itr->second.downloaded, itr->second.maxSize ) ); + } + } + + downloadList.SetVirtualCountAsync( (int)listContents.size() ); +// Navigation_ShowService( SERVICE_DOWNLOADS, SHOWMODE_AUTO ); +} + +static void CleanupDownloads() +{ + { + AutoLock lock( downloadedFiles ); + DownloadList::DownloadedFileList &downloads = downloadedFiles.downloadList; + DownloadList::iterator itr, next; + for ( itr = downloads.begin(); itr != downloads.end();) + { + next = itr; + ++next; + if ( !PathFileExists( itr->path ) ) + downloads.erase( itr ); + else + itr = next; + } + } + +// Navigation_ShowService(SERVICE_DOWNLOADS, SHOWMODE_AUTO); +} + +void Downloads_UpdateStatusBar(HWND hwndDlg) +{ + wchar_t status[256]=L""; + downloadStatus.GetStatusString(status, 256); + SetWindowText(GetDlgItem(hwndDlg, IDC_STATUS), status); +} + +void Downloads_Paint(HWND hwndDlg) +{ + int tab[] = { IDC_DOWNLOADLIST | DCW_SUNKENBORDER, }; + dialogSkinner.Draw(hwndDlg, tab, sizeof(tab) / sizeof(tab[0])); +} + + +static HRGN g_rgnUpdate = NULL; +static int offsetX = 0, offsetY = 0; + +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; } +#define SETLAYOUTFLAGS(_layout, _r) \ + { \ + BOOL fVis; \ + fVis = (WS_VISIBLE & (LONG)GetWindowLongPtr(_layout->hwnd, GWL_STYLE)); \ + if (_layout->x == _r.left && _layout->y == _r.top) _layout->flags |= SWP_NOMOVE; \ + if (_layout->cx == (_r.right - _r.left) && _layout->cy == (_r.bottom - _r.top)) _layout->flags |= SWP_NOSIZE; \ + if ((SWP_HIDEWINDOW & _layout->flags) && !fVis) _layout->flags &= ~SWP_HIDEWINDOW; \ + if ((SWP_SHOWWINDOW & _layout->flags) && fVis) _layout->flags &= ~SWP_SHOWWINDOW; \ + } + +#define LAYOUTNEEEDUPDATE(_layout) ((SWP_NOMOVE | SWP_NOSIZE) != ((SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW | SWP_SHOWWINDOW) & _layout->flags)) + +#define GROUP_MIN 0x1 +#define GROUP_MAX 0x2 +#define GROUP_STATUSBAR 0x1 +#define GROUP_MAIN 0x2 + +static void LayoutWindows(HWND hwnd, BOOL fRedraw, BOOL fUpdateAll = FALSE) +{ + static INT controls[] = + { + GROUP_STATUSBAR, IDC_PLAY, IDC_ENQUEUE, IDC_CUSTOM, IDC_REMOVE, IDC_CLEANUP, IDC_STATUS, + GROUP_MAIN, IDC_DOWNLOADLIST + }; + + INT index; + RECT rc, rg, ri; + LAYOUT layout[sizeof(controls)/sizeof(controls[0])], *pl; + BOOL skipgroup; + HRGN rgn = NULL; + + GetClientRect(hwnd, &rc); + + if (rc.right == rc.left || rc.bottom == rc.top) + return; + + if ( rc.right > WASABI_API_APP->getScaleX( 4 ) ) + rc.right -= WASABI_API_APP->getScaleX( 4 ); + + SetRect( &rg, rc.left, rc.top, rc.right, rc.top ); + + pl = layout; + skipgroup = FALSE; + + InvalidateRect(hwnd, NULL, TRUE); + + for (index = 0; index < sizeof(controls) / sizeof(*controls); index++) + { + if (controls[index] >= GROUP_MIN && controls[index] <= GROUP_MAX) // group id + { + skipgroup = FALSE; + switch (controls[index]) + { + case GROUP_STATUSBAR: + { + wchar_t buffer[128] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDC_PLAY, buffer, ARRAYSIZE(buffer)); + LRESULT idealSize = MLSkinnedButton_GetIdealSize(GetDlgItem(hwnd, IDC_PLAY), buffer); + + SetRect(&rg, rc.left + WASABI_API_APP->getScaleX(1), + rc.bottom - WASABI_API_APP->getScaleY(HIWORD(idealSize)), + rc.right, rc.bottom); + rc.bottom = rg.top - WASABI_API_APP->getScaleY(3); + break; + } + case GROUP_MAIN: + SetRect(&rg, rc.left + WASABI_API_APP->getScaleX(1), rc.top, rc.right, rc.bottom); + break; + } + continue; + } + if (skipgroup) continue; + + pl->id = controls[index]; + pl->hwnd = GetDlgItem(hwnd, pl->id); + + if ( !pl->hwnd ) + continue; + + 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_PLAY: + case IDC_ENQUEUE: + case IDC_CUSTOM: + case IDC_REMOVE: + case IDC_CLEANUP: + if ( IDC_CUSTOM != pl->id || customAllowed ) + { + if ( groupBtn && pl->id == IDC_PLAY && enqueuedef == 1 ) + { + pl->flags |= SWP_HIDEWINDOW; + break; + } + + if ( groupBtn && pl->id == IDC_ENQUEUE && enqueuedef != 1 ) + { + pl->flags |= SWP_HIDEWINDOW; + break; + } + + if ( groupBtn && ( pl->id == IDC_PLAY || pl->id == IDC_ENQUEUE ) && customAllowed ) + { + pl->flags |= SWP_HIDEWINDOW; + break; + } + + wchar_t buffer[ 128 ] = { 0 }; + GetWindowTextW( pl->hwnd, buffer, ARRAYSIZE( buffer ) ); + + LRESULT idealSize = MLSkinnedButton_GetIdealSize( pl->hwnd, buffer ); + LONG width = LOWORD( idealSize ) + WASABI_API_APP->getScaleX( 6 ); + + SETLAYOUTPOS( pl, rg.left, rg.bottom - WASABI_API_APP->getScaleY( HIWORD( idealSize ) ), width, WASABI_API_APP->getScaleY( HIWORD( idealSize ) ) ); + + pl->flags |= ( ( rg.right - rg.left ) > width ) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW; + + if ( SWP_SHOWWINDOW & pl->flags ) + rg.left += ( pl->cx + WASABI_API_APP->getScaleX( 4 ) ); + } + else + pl->flags |= SWP_HIDEWINDOW; + break; + case IDC_STATUS: + SETLAYOUTPOS(pl, rg.left, rg.top, rg.right - rg.left, (rg.bottom - rg.top)); + pl->flags |= (pl->cx > 16) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW; + break; + case IDC_DOWNLOADLIST: + pl->flags |= (rg.top < rg.bottom) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW; + SETLAYOUTPOS( pl, rg.left, rg.top + WASABI_API_APP->getScaleY( 1 ), rg.right - rg.left + WASABI_API_APP->getScaleY( 1 ), ( rg.bottom - rg.top ) - WASABI_API_APP->getScaleY( 2 ) ); + break; + } + + SETLAYOUTFLAGS(pl, ri); + if ( LAYOUTNEEEDUPDATE( pl ) ) + { + if ( SWP_NOSIZE == ( ( SWP_HIDEWINDOW | SWP_SHOWWINDOW | SWP_NOSIZE ) & pl->flags ) && ri.left == ( pl->x + offsetX ) && ri.top == ( pl->y + offsetY ) && IsWindowVisible( pl->hwnd ) ) + { + SetRect( &ri, pl->x, pl->y, pl->cx + pl->x, pl->y + pl->cy ); + ValidateRect( hwnd, &ri ); + } + + pl++; + } + else if ( ( fRedraw || ( !offsetX && !offsetY ) ) && IsWindowVisible( pl->hwnd ) ) + { + ValidateRect( hwnd, &ri ); + if ( GetUpdateRect( pl->hwnd, NULL, FALSE ) ) + { + if ( !rgn ) + rgn = CreateRectRgn( 0, 0, 0, 0 ); + + GetUpdateRgn( pl->hwnd, rgn, FALSE ); + OffsetRgn( rgn, pl->x, pl->y ); + InvalidateRgn( hwnd, rgn, FALSE ); + } + } + } + + if (pl != layout) + { + LAYOUT *pc; + HDWP hdwp = BeginDeferWindowPos((INT)(pl - layout)); + for(pc = layout; pc < pl && hdwp; pc++) + { + hdwp = DeferWindowPos(hdwp, pc->hwnd, NULL, pc->x, pc->y, pc->cx, pc->cy, pc->flags); + } + + if (hdwp) + EndDeferWindowPos(hdwp); + + if ( !rgn ) + rgn = CreateRectRgn( 0, 0, 0, 0 ); + + if (fRedraw) + { + GetUpdateRgn(hwnd, rgn, FALSE); + for ( pc = layout; pc < pl && hdwp; pc++ ) + { + if ( pc->rgn ) + { + OffsetRgn( pc->rgn, pc->x, pc->y ); + CombineRgn( rgn, rgn, pc->rgn, RGN_OR ); + } + } + + RedrawWindow(hwnd, NULL, rgn, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASENOW | RDW_ALLCHILDREN); + } + + if (g_rgnUpdate) + { + GetUpdateRgn(hwnd, g_rgnUpdate, FALSE); + for(pc = layout; pc < pl && hdwp; pc++) + { + if (pc->rgn) + { + OffsetRgn(pc->rgn, pc->x, pc->y); + CombineRgn(g_rgnUpdate, g_rgnUpdate, pc->rgn, RGN_OR); + } + } + } + + for(pc = layout; pc < pl && hdwp; pc++) if (pc->rgn) DeleteObject(pc->rgn); + } + + if ( rgn ) + DeleteObject( rgn ); + + ValidateRgn(hwnd, NULL); +} + +void Downloads_DisplayChange(HWND hwndDlg) +{ + ListView_SetTextColor(downloadList.getwnd(), dialogSkinner.Color(WADLG_ITEMFG)); + ListView_SetBkColor(downloadList.getwnd(), dialogSkinner.Color(WADLG_ITEMBG)); + ListView_SetTextBkColor(downloadList.getwnd(), dialogSkinner.Color(WADLG_ITEMBG)); + downloadList.SetFont(dialogSkinner.GetFont()); + LayoutWindows(hwndDlg, TRUE); +} + +static void DownloadsDialog_SkinControls(HWND hwnd, const INT *itemList, INT itemCount, UINT skinType, UINT skinStyle) +{ + MLSKINWINDOW skinWindow = {0}; + skinWindow.style = skinStyle; + skinWindow.skinType = skinType; + + for(INT i = 0; i < itemCount; i++) + { + skinWindow.hwndToSkin = GetDlgItem(hwnd, itemList[i]); + if (NULL != skinWindow.hwndToSkin) + { + MLSkinWindow(plugin.hwndLibraryParent, &skinWindow); + } + } +} + +static void DownloadDialog_InitializeList(HWND hwnd) +{ + HWND hControl = GetDlgItem(hwnd, IDC_DOWNLOADLIST); + if (NULL == hControl) return; + + UINT styleEx = (UINT)GetWindowLongPtr(hControl, GWL_EXSTYLE); + SetWindowLongPtr(hControl, GWL_EXSTYLE, styleEx & ~WS_EX_NOPARENTNOTIFY); + + styleEx = LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP; + SendMessage(hControl, LVM_SETEXTENDEDLISTVIEWSTYLE, styleEx, styleEx); + SendMessage(hControl, LVM_SETUNICODEFORMAT, (WPARAM)TRUE, 0L); + + MLSKINWINDOW skinWindow; + skinWindow.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWLVS_ALTERNATEITEMS; + skinWindow.skinType = SKINNEDWND_TYPE_LISTVIEW; + skinWindow.hwndToSkin = hControl; + MLSkinWindow(plugin.hwndLibraryParent, &skinWindow); +} + +bool COL_SOURCE_Sort(const DownloadListItem* item1, const DownloadListItem* item2) +{ + if (item1->f && item2->f) + return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, (item1->f->source), -1, (item2->f->source), -1)); + else if (!item1->f && !item2->f) + return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, (item1->source), -1, (item2->source), -1)); + else if (!item1->f) + return (FALSE == downloadsSortAscending)?0:1; + else //if (!item2->f) + return (FALSE == downloadsSortAscending)?1:0; + + //return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, + // (item1->f?item1->f->source:item1->source), -1, + // (item2->f?item2->f->source:item2->source), -1)); +} + +bool COL_TITLE_Sort(const DownloadListItem* item1, const DownloadListItem* item2) +{ + if (item1->f && item2->f) + return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, (item1->f->title), -1, (item2->f->title), -1)); + else if (!item1->f && !item2->f) + return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, (item1->title), -1, (item2->title), -1)); + else if (!item1->f) + return (FALSE == downloadsSortAscending)?0:1; + else //if (!item2->f) + return (FALSE == downloadsSortAscending)?1:0; + + //return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, + // (item1->f?item1->f->title:item1->title), -1, + // (item2->f?item2->f->title:item2->title), -1)); +} + +bool COL_PROGRESS_Sort( const DownloadListItem *item1, const DownloadListItem *item2 ) +{ + if ( item1->f && item2->f ) + return ( item1->f->downloadStatus > item2->f->downloadStatus ); + else if ( !item1->f && !item2->f ) + return ( item1->token < item2->token ); + else if ( !item1->f ) + return ( FALSE == downloadsSortAscending ) ? 0 : 1; + else //if (!item2->f) + return ( FALSE == downloadsSortAscending ) ? 1 : 0; + + //return ((item1->f?item1->f->downloadStatus:-1) < (item2->f?item2->f->downloadStatus:-1)); + + //return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, + // (item1->f?GetDownloadStatus(item1->f->downloadStatus):item1->status), -1, + // (item2->f?GetDownloadStatus(item2->f->downloadStatus):item2->status), -1)); +} + +bool COL_PATH_Sort(const DownloadListItem* item1, const DownloadListItem* item2) +{ + if (item1->f && item2->f) + return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, (item1->f->path), -1, (item2->f->path), -1)); + else if (!item1->f && !item2->f) + return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, (item1->path), -1, (item2->path), -1)); + else if (!item1->f) + return (FALSE == downloadsSortAscending)?0:1; + else //if (!item2->f) + return (FALSE == downloadsSortAscending)?1:0; + + //return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, + // (item1->f?item1->f->path:item1->path), -1, + // (item2->f?item2->f->path:item2->path), -1)); +} + +bool COL_DATE_Sort(const DownloadListItem* item1, const DownloadListItem* item2) +{ + if (item1->f && item2->f) + return item1->f->downloadDate < item2->f->downloadDate; + else if (!item1->f && !item2->f) + return item1->token < item2->token; + else if (!item1->f) + return (FALSE == downloadsSortAscending)?0:1; + else //if (!item2->f) + return (FALSE == downloadsSortAscending)?1:0; +} + +bool COL_SIZE_Sort(const DownloadListItem* item1, const DownloadListItem* item2) +{ + if (item1->f && item2->f) + return item1->f->totalSize < item2->f->totalSize; + else if (!item1->f && !item2->f) + return item1->token < item2->token; + else if (!item1->f) + return (FALSE == downloadsSortAscending)?0:1; + else //if (!item2->f) + return (FALSE == downloadsSortAscending)?1:0; +} + +static BOOL Downloads_SortItems(int sortColumn) +{ + AutoLock lock (downloadedFiles); + switch (sortColumn) + { + case COL_TITLE: + std::sort(listContents.begin(), listContents.end(), COL_TITLE_Sort); + if (FALSE == downloadsSortAscending) std::reverse(listContents.begin(), listContents.end()); + return TRUE; + case COL_PROGRESS: + std::sort(listContents.begin(), listContents.end(), COL_PROGRESS_Sort); + if (FALSE == downloadsSortAscending) std::reverse(listContents.begin(), listContents.end()); + return TRUE; + case COL_DATE: + std::sort(listContents.begin(), listContents.end(), COL_DATE_Sort); + if (FALSE == downloadsSortAscending) std::reverse(listContents.begin(), listContents.end()); + return TRUE; + case COL_SOURCE: + std::sort(listContents.begin(), listContents.end(), COL_SOURCE_Sort); + if (FALSE == downloadsSortAscending) std::reverse(listContents.begin(), listContents.end()); + return TRUE; + case COL_SIZE: + std::sort(listContents.begin(), listContents.end(), COL_SIZE_Sort); + if (FALSE == downloadsSortAscending) std::reverse(listContents.begin(), listContents.end()); + return TRUE; + case COL_PATH: + std::sort(listContents.begin(), listContents.end(), COL_PATH_Sort); + if (FALSE == downloadsSortAscending) std::reverse(listContents.begin(), listContents.end()); + return TRUE; + } + return FALSE; +} + +static void Downloads_SetListSortColumn(HWND hwnd, INT listId, INT index, BOOL fAscending) +{ + HWND hItems = GetDlgItem(hwnd, listId); + if (NULL == hItems) return; + + HWND hHeader = (HWND)SNDMSG(hItems, LVM_GETHEADER, 0, 0L); + if (NULL == hHeader) return; + + HDITEM item; + item.mask = HDI_FORMAT; + // reset first (ml req) + INT count = (INT)SNDMSG(hHeader, HDM_GETITEMCOUNT, 0, 0L); + for (INT i = 0; i < count; i++) + { + if (index != i && FALSE != (BOOL)SNDMSG(hHeader, HDM_GETITEM, i, (LPARAM)&item)) + { + if (0 != ((HDF_SORTUP | HDF_SORTDOWN) & item.fmt)) + { + item.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN); + SNDMSG(hHeader, HDM_SETITEM, i, (LPARAM)&item); + } + } + } + + if (FALSE != (BOOL)SNDMSG(hHeader, HDM_GETITEM, index, (LPARAM)&item)) + { + INT fmt = item.fmt & ~(HDF_SORTUP | HDF_SORTDOWN); + fmt |= (FALSE == fAscending) ? HDF_SORTDOWN : HDF_SORTUP; + if (fmt != item.fmt) + { + item.fmt = fmt; + SNDMSG(hHeader, HDM_SETITEM, index, (LPARAM)&item); + } + } +} + +static BOOL Downloads_Sort(HWND hwnd, INT iColumn, bool fAscending) +{ + BOOL result = TRUE; + downloadsSortAscending = fAscending; + Downloads_SortItems(iColumn); + Downloads_SetListSortColumn(hwnd, IDC_DOWNLOADLIST, iColumn, fAscending); + + if (FALSE != result) + { + HWND hItems = GetDlgItem(hwnd, IDC_DOWNLOADLIST); + if (NULL != hItems) + InvalidateRect(hItems, NULL, TRUE); + } + + return TRUE; +} + +void Downloads_UpdateButtonText(HWND hwndDlg, int _enqueuedef) +{ + if (groupBtn) + { + switch(_enqueuedef) + { + case 1: + SetDlgItemTextW(hwndDlg, IDC_PLAY, view.enqueue); + customAllowed = FALSE; + break; + + default: + // v5.66+ - re-use the old predixis parts so the button can be used functionally via ml_enqplay + // pass the hwnd, button id and plug-in id so the ml plug-in can check things as needed + pluginMessage p = {ML_MSG_VIEW_BUTTON_HOOK_IN_USE, (INT_PTR)_enqueuedef, 0, 0}; + + wchar_t *pszTextW = (wchar_t *)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p); + if (pszTextW && pszTextW[0] != 0) + { + // set this to be a bit different so we can just use one button and not the + // mixable one as well (leaving that to prevent messing with the resources) + SetDlgItemTextW(hwndDlg, IDC_PLAY, pszTextW); + customAllowed = TRUE; + } + else + { + SetDlgItemTextW(hwndDlg, IDC_PLAY, view.play); + customAllowed = FALSE; + } + break; + } + } +} + +static void Downloads_ManageButtons( HWND hwndDlg ) +{ + int has_selection = downloadList.GetSelectedCount(); + + const int buttonids[] = { IDC_PLAY, IDC_ENQUEUE, IDC_CUSTOM, IDC_REMOVE }; + for ( size_t i = 0; i != sizeof( buttonids ) / sizeof( buttonids[ 0 ] ); i++ ) + { + HWND controlHWND = GetDlgItem( hwndDlg, buttonids[ i ] ); + EnableWindow( controlHWND, has_selection ); + } +} + +void Downloads_Init(HWND hwndDlg) +{ + HWND hLibrary = plugin.hwndLibraryParent; + downloads_window = hwndDlg; + + if (!view.play) + { + SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_GET_VIEW_BUTTON_TEXT, (WPARAM)&view); + } + + HACCEL accel = WASABI_API_LOADACCELERATORSW(IDR_VIEW_DOWNLOAD_ACCELERATORS); + if (accel) + WASABI_API_APP->app_addAccelerators(hwndDlg, &accel, 1, TRANSLATE_MODE_CHILD); + + g_context_menus2 = WASABI_API_LOADMENU(IDR_MENU1); + groupBtn = ML_GROUPBTN_VAL(); + enqueuedef = (ML_ENQDEF_VAL() == 1); + + // v5.66+ - re-use the old predixis parts so the button can be used functionally via ml_enqplay + // pass the hwnd, button id and plug-in id so the ml plug-in can check things as needed + pluginMessage p = {ML_MSG_VIEW_BUTTON_HOOK, (INT_PTR)hwndDlg, (INT_PTR)MAKELONG(IDC_CUSTOM, IDC_ENQUEUE), (INT_PTR)L"ml_downloads"}; + wchar_t *pszTextW = (wchar_t *)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p); + if (pszTextW && pszTextW[0] != 0) + { + // set this to be a bit different so we can just use one button and not the + // mixable one as well (leaving that to prevent messing with the resources) + customAllowed = TRUE; + SetDlgItemTextW(hwndDlg, IDC_CUSTOM, pszTextW); + } + else + customAllowed = FALSE; + + MLSkinWindow2(hLibrary, hwndDlg, SKINNEDWND_TYPE_AUTO, SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS); + + const INT szControls[] = {IDC_PLAY, IDC_ENQUEUE, IDC_CUSTOM}; + DownloadsDialog_SkinControls(hwndDlg, szControls, ARRAYSIZE(szControls), SKINNEDWND_TYPE_AUTO, + SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | (groupBtn ? SWBS_SPLITBUTTON : 0)); + + const INT szControlz[] = {IDC_REMOVE, IDC_CLEANUP, IDC_STATUS}; + DownloadsDialog_SkinControls(hwndDlg, szControlz, ARRAYSIZE(szControlz), SKINNEDWND_TYPE_AUTO, + SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS); + + DownloadDialog_InitializeList(hwndDlg); + Downloads_UpdateStatusBar(hwndDlg); + + downloadList.setwnd(GetDlgItem(hwndDlg, IDC_DOWNLOADLIST)); + downloadList.AddCol(WASABI_API_LNGSTRINGW(IDS_TITLE), downloadsTitleWidth); + downloadList.AddCol(WASABI_API_LNGSTRINGW(IDS_PROGRESS), downloadsProgressWidth); + downloadList.AddCol(WASABI_API_LNGSTRINGW(IDS_DATE), downloadsDateWidth); + downloadList.AddCol(WASABI_API_LNGSTRINGW(IDS_SOURCE), downloadsSourceWidth); + downloadList.AddCol(WASABI_API_LNGSTRINGW(IDS_SIZE), downloadsSizeWidth); + downloadList.AddCol(WASABI_API_LNGSTRINGW(IDS_PATH), downloadsPathWidth); + + DownloadsUpdated(); + + downloadList.SetVirtualCount((int)listContents.size()); + Downloads_UpdateButtonText(hwndDlg, enqueuedef == 1); + Downloads_ManageButtons(hwndDlg); + Downloads_DisplayChange(hwndDlg); + Downloads_Sort(hwndDlg, downloadsItemSort, downloadsSortAscending); + SetTimer(hwndDlg, DOWNLOADSDIALOG_TIMER_UPDATESTATUSBAR , 1000, 0); +} + +void Downloads_Timer( HWND hwndDlg, UINT timerId ) +{ + switch ( timerId ) + { + case DOWNLOADSDIALOG_TIMER_UPDATESTATUSBAR: + Downloads_UpdateStatusBar( hwndDlg ); + { + AutoLock lock( downloadStatus.statusLock ); + for ( DownloadListItem *l_content : listContents ) + { + if ( l_content->token ) + { + size_t d = downloadStatus.downloads[ l_content->token ].downloaded; + size_t s = downloadStatus.downloads[ l_content->token ].maxSize; + + if ( s ) + StringCchPrintf( l_content->status, 20, WASABI_API_LNGSTRINGW( IDS_DOWNLOADING_PERCENT ), (int)( d / ( s / 100 ) ) ); + else + { + if ( WAC_API_DOWNLOADMANAGER->IsPending( l_content->token ) ) + WASABI_API_LNGSTRINGW_BUF( IDS_DOWNLOAD_PENDING, l_content->status, 20 ); + else + WASABI_API_LNGSTRINGW_BUF( IDS_DOWNLOADING, l_content->status, 20 ); + } + + PostMessage( downloadList.getwnd(), LVM_REDRAWITEMS, 0, listContents.size() ); + } + } + } + break; + } +} + +static INT Downloads_GetListSortColumn(HWND hwnd, INT listId, bool *fAscending) +{ + HWND hItems = GetDlgItem(hwnd, listId); + if (NULL != hItems) + { + HWND hHeader = (HWND)SNDMSG(hItems, LVM_GETHEADER, 0, 0L); + if (NULL != hHeader) + { + HDITEM item; + item.mask = HDI_FORMAT; + + INT count = (INT)SNDMSG(hHeader, HDM_GETITEMCOUNT, 0, 0L); + for (INT i = 0; i < count; i++) + { + if (FALSE != (BOOL)SNDMSG(hHeader, HDM_GETITEM, i, (LPARAM)&item) && + 0 != ((HDF_SORTUP | HDF_SORTDOWN) & item.fmt)) + { + if (NULL != fAscending) + { + *fAscending = (0 != (HDF_SORTUP & item.fmt)); + } + return i; + } + } + } + } + return -1; +} + +void Downloads_Destroy( HWND hwndDlg ) +{ + downloads_window = 0; + downloadsSourceWidth = downloadList.GetColumnWidth( COL_SOURCE ); + downloadsTitleWidth = downloadList.GetColumnWidth( COL_TITLE ); + downloadsProgressWidth = downloadList.GetColumnWidth( COL_PROGRESS ); + downloadsPathWidth = downloadList.GetColumnWidth( COL_PATH ); + downloadsDateWidth = downloadList.GetColumnWidth( COL_DATE ); + downloadsSizeWidth = downloadList.GetColumnWidth( COL_SIZE ); + + for ( DownloadListItem *l_content : listContents ) + delete l_content; + + listContents.clear(); + + downloadList.setwnd( NULL ); + + bool fAscending; + downloadsItemSort = Downloads_GetListSortColumn( hwndDlg, IDC_DOWNLOADLIST, &fAscending ); + downloadsSortAscending = ( -1 != downloadsItemSort ) ? ( FALSE != fAscending ) : true; + + int activeDownloads = 0; + int historyDownloads = 0; + { + Nullsoft::Utility::AutoLock historylock( downloadedFiles.downloadedLock ); + Nullsoft::Utility::AutoLock statuslock( downloadStatus.statusLock ); + historyDownloads = (int)downloadedFiles.downloadList.size(); + activeDownloads = (int)downloadStatus.downloads.size(); + } + + if ( !activeDownloads && !historyDownloads && !no_auto_hide ) + { + HNAVITEM hItem = MLNavCtrl_FindItemById( plugin.hwndLibraryParent, downloads_treeItem ); + if ( hItem ) + { + MLNavCtrl_DeleteItem( plugin.hwndLibraryParent, hItem ); + downloads_treeItem = 0; + } + } +} + +void Downloads_Remove( bool del = false, HWND parent = NULL ) +{ + int d = -1; + int r = 0; + while ( GetDownload( d ) ) + { + int download = d - r; + DownloadListItem *item = listContents[ download ]; + if ( item->f ) + { + AutoLock lock( downloadedFiles ); + int j = 0; + for ( DownloadList::iterator i = downloadedFiles.begin(); i != downloadedFiles.end(); ++i ) + { + if ( !_wcsicmp( i->path, item->f->path ) ) + { + if ( del ) + { + if ( !downloadedFiles.RemoveAndDelete( j ) ) + MessageBox( parent, WASABI_API_LNGSTRINGW( IDS_DELETEFAILED ), downloadedFiles.downloadList[ j ].path, 0 ); + } + else + downloadedFiles.Remove( j ); + + delete item; + listContents.erase( listContents.begin() + download ); + r++; + dirty++; + + break; + } + + ++j; + } + } + else if ( item->token ) + { + AutoLock lock( downloadStatus.statusLock ); + downloadStatus.downloads[ item->token ].killswitch = 1; + delete item; + listContents.erase( listContents.begin() + download ); + r++; + } + else + { + delete item; + listContents.erase( listContents.begin() + download ); + r++; + } + } + + downloadList.SetVirtualCountAsync( (int)listContents.size() ); + downloadList.UnselectAll(); + // Navigation_ShowService(SERVICE_DOWNLOADS, SHOWMODE_AUTO); +} + +void Downloads_Delete( HWND parent ) +{ + wchar_t message[ 256 ] = { 0 }; + int c = downloadList.GetSelectedCount(); + + if ( !c ) + return; + else if ( c == 1 ) + WASABI_API_LNGSTRINGW_BUF( IDS_PERM_DELETE_ARE_YOU_SURE, message, 256 ); + else + StringCchPrintf( message, 256, WASABI_API_LNGSTRINGW( IDS_PERM_DELETE_THESE_ARE_YOU_SURE ), c ); + + if ( MessageBox( NULL, message, WASABI_API_LNGSTRINGW( IDS_DELETION ), MB_ICONWARNING | MB_YESNO ) == IDNO ) + return; + + Downloads_Remove( true, parent ); +} + +void Downloads_CleanUp(HWND hwndDlg) +{ + wchar_t titleStr[64] = {0}; + if ( MessageBox( hwndDlg, WASABI_API_LNGSTRINGW( IDS_CLEAR_ALL_FINISHED_DOWNLOADS ), WASABI_API_LNGSTRINGW_BUF( IDS_CLEAN_UP_LIST, titleStr, 64 ), MB_ICONWARNING | MB_YESNO ) == IDNO ) + return; + + { + AutoLock lock( downloadedFiles ); + downloadedFiles.downloadList.clear(); + } + + dirty++; + + DownloadsUpdated(); +} + +void Downloads_InfoBox( HWND parent ) +{ + int download = -1; + if ( GetDownload( download ) ) + { + const wchar_t *fn; + if ( listContents[ download ]->f ) + fn = listContents[ download ]->f->path; + else + fn = listContents[ download ]->path; + + if ( fn ) + { + infoBoxParamW p = { parent, fn }; + SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&p, IPC_INFOBOXW ); + } + } +} + +void Downloads_SelectAll() +{ + int l = downloadList.GetCount(); + for ( int i = 0; i < l; i++ ) + downloadList.SetSelected( i ); +} + +static void exploreItemFolder( HWND hwndDlg ) +{ + if ( downloadList.GetSelectionMark() >= 0 ) + { + int download = -1; + while ( GetDownload( download ) ) + { + wchar_t *file; + if ( listContents[ download ]->f ) + file = listContents[ download ]->f->path; + else + file = listContents[ download ]->path; + + WASABI_API_EXPLORERFINDFILE->AddFile( file ); + } + WASABI_API_EXPLORERFINDFILE->ShowFiles(); + } +} + +void Downloads_Cancel() +{ + int l_selected_count = downloadList.GetSelectedCount(); + for ( int i = -1; i < l_selected_count; ++i ) + { + if ( GetDownload( i ) ) + { + dirty++; + if ( !listContents[ i ]->f ) + WAC_API_DOWNLOADMANAGER->CancelDownload( listContents[ i ]->token ); + + break; // Workaround for 5.9.1 to avoid crash if cancel of many downloads in same time + } + } +} + +int we_are_drag_and_dropping = 0; + +static void Downloads_OnColumnClick(HWND hwnd, NMLISTVIEW *plv) +{ + bool fAscending; + INT iSort = Downloads_GetListSortColumn(hwnd, IDC_DOWNLOADLIST, &fAscending); + fAscending = (-1 != iSort && iSort == plv->iSubItem) ? (!fAscending) : true; + Downloads_Sort(hwnd, plv->iSubItem, fAscending); +} + +LRESULT DownloadList_Notify( LPNMHDR l, HWND hwndDlg ) +{ + switch ( l->code ) + { + case LVN_COLUMNCLICK: + Downloads_OnColumnClick( hwndDlg, (NMLISTVIEW *)l ); + break; + case NM_DBLCLK: + Downloads_Play( ( ( !!( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) ) ^ ML_ENQDEF_VAL() ) ); + break; + case LVN_BEGINDRAG: + we_are_drag_and_dropping = 1; + SetCapture( hwndDlg ); + break; + case LVN_ITEMCHANGED: + Downloads_ManageButtons( hwndDlg ); + break; + case LVN_GETDISPINFO: + NMLVDISPINFO *lpdi = (NMLVDISPINFO *)l; + size_t item = lpdi->item.iItem; + + if ( item < 0 || item >= listContents.size() ) + return 0; + + //if (FALSE == downloadsSortAscending) item = listContents.size() - item - 1; + + DownloadListItem *l = listContents[ item ]; + + if ( lpdi->item.mask & LVIF_TEXT ) + { + lpdi->item.pszText[ 0 ] = 0; + switch ( lpdi->item.iSubItem ) + { + case COL_TITLE: + if ( !l->token && l->f ) + { + wchar_t *l_title = L""; + + if ( l->f->title != NULL ) + l_title = l->f->title; + + lstrcpyn( lpdi->item.pszText, l_title, lpdi->item.cchTextMax ); + } + else + { + if ( l->title ) lstrcpyn( lpdi->item.pszText, l->title, lpdi->item.cchTextMax ); + } + break; + case COL_PROGRESS: + if ( !l->token && l->f ) + { + switch ( l->f->downloadStatus ) + { + case DownloadedFile::DOWNLOAD_SUCCESS: + WASABI_API_LNGSTRINGW_BUF( IDS_DOWNLOAD_SUCCESS, lpdi->item.pszText, lpdi->item.cchTextMax ); + break; + case DownloadedFile::DOWNLOAD_FAILURE: + WASABI_API_LNGSTRINGW_BUF( IDS_DOWNLOAD_FAILURE, lpdi->item.pszText, lpdi->item.cchTextMax ); + break; + case DownloadedFile::DOWNLOAD_CANCELED: + WASABI_API_LNGSTRINGW_BUF( IDS_DOWNLOAD_CANCELED, lpdi->item.pszText, lpdi->item.cchTextMax ); + break; + } + } + else lstrcpyn( lpdi->item.pszText, l->status, lpdi->item.cchTextMax ); + break; + case COL_DATE: + { + if ( !l->token && l->f && l->f->downloadDate ) + { + wchar_t tmp[ 128 ] = { 0 }; + MakeDateString( l->f->downloadDate, tmp, 128 ); + lstrcpyn( lpdi->item.pszText, tmp, lpdi->item.cchTextMax ); + } + else + { + WASABI_API_LNGSTRINGW_BUF( IDS_N_A, lpdi->item.pszText, lpdi->item.cchTextMax ); + } + break; + } + case COL_SOURCE: + if ( !l->token && l->f ) + { + wchar_t *l_source = L""; + + if ( l->f->source != NULL ) + l_source = l->f->source; + + lstrcpyn( lpdi->item.pszText, l_source, lpdi->item.cchTextMax ); + } + else + lstrcpyn( lpdi->item.pszText, l->source, lpdi->item.cchTextMax ); + break; + case COL_SIZE: + { + if ( !l->token && l->f && l->f->totalSize > 0 ) + WASABI_API_LNG->FormattedSizeString( lpdi->item.pszText, lpdi->item.cchTextMax, l->f->totalSize ); + else + WASABI_API_LNGSTRINGW_BUF( IDS_N_A, lpdi->item.pszText, lpdi->item.cchTextMax ); + break; + } + case COL_PATH: + if ( !l->token && l->f ) + { + wchar_t *l_path = L""; + + if ( l->f->path != NULL ) + l_path = l->f->path; + + lstrcpyn( lpdi->item.pszText, l_path, lpdi->item.cchTextMax ); + } + else + { + if ( l->path ) + lstrcpyn( lpdi->item.pszText, l->path, lpdi->item.cchTextMax ); + } + + break; + } + } + + break; + } + + return 0; +} + +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 *new_buf = (wchar_t *)realloc( *buf, ( buf_size + 1 ) * sizeof( wchar_t ) ); + if ( new_buf ) + { + *buf = new_buf; + } + else + { + new_buf = (wchar_t*)calloc( ( buf_size + 1 ), sizeof( wchar_t ) ); + if ( new_buf ) + { + memcpy( new_buf, *buf, ( old_buf_size * sizeof( wchar_t ) ) ); + free( *buf ); + *buf = new_buf; + } + else buf_size = (int)old_buf_size; + } + } + + StringCchCopyW( *buf + buf_pos, buf_size, tbuf ); + buf_pos = newsize; +} + +wchar_t *getSelectedList() +{ + wchar_t *path = NULL; + int buf_pos = 0; + int 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 SwapPlayEnqueueInMenu( HMENU listMenu ) +{ + int playPos = -1, enqueuePos = -1; + MENUITEMINFOW playItem = { sizeof( MENUITEMINFOW ), 0, }, enqueueItem = { sizeof( MENUITEMINFOW ), 0, }; + + int numItems = GetMenuItemCount( listMenu ); + + for ( int i = 0; i < numItems; i++ ) + { + UINT id = GetMenuItemID( listMenu, i ); + if ( id == IDC_PLAY ) + { + playItem.fMask = MIIM_ID; + playPos = i; + GetMenuItemInfoW( listMenu, i, TRUE, &playItem ); + } + else if ( id == IDC_ENQUEUE ) + { + enqueueItem.fMask = MIIM_ID; + enqueuePos = i; + GetMenuItemInfoW( listMenu, i, TRUE, &enqueueItem ); + } + } + + playItem.wID = IDC_ENQUEUE; + enqueueItem.wID = IDC_PLAY; + SetMenuItemInfoW( listMenu, playPos, TRUE, &playItem ); + SetMenuItemInfoW( listMenu, enqueuePos, TRUE, &enqueueItem ); +} + +static void SyncMenuWithAccelerators( HWND hwndDlg, HMENU menu ) +{ + HACCEL szAccel[ 24 ] = { 0 }; + INT c = WASABI_API_APP->app_getAccelerators( hwndDlg, szAccel, sizeof( szAccel ) / sizeof( szAccel[ 0 ] ), FALSE ); + AppendMenuShortcuts( menu, szAccel, c, MSF_REPLACE ); +} + +void UpdateMenuItems( HWND hwndDlg, HMENU menu ) +{ + bool swapPlayEnqueue = false; + if ( ML_ENQDEF_VAL() ) + { + SwapPlayEnqueueInMenu( menu ); + swapPlayEnqueue = true; + } + + SyncMenuWithAccelerators( hwndDlg, menu ); + if ( swapPlayEnqueue ) + SwapPlayEnqueueInMenu( menu ); +} + +static int IPC_LIBRARY_SENDTOMENU = 0; +static librarySendToMenuStruct s = { 0 }; + +static void DownloadList_RightClick(HWND hwndDlg, HWND listHwnd, POINTS pts) +{ + POINT pt; + POINTSTOPOINT(pt, pts); + + RECT controlRect, headerRect; + if (FALSE == GetClientRect(listHwnd, &controlRect)) + SetRectEmpty(&controlRect); + else + MapWindowPoints(listHwnd, HWND_DESKTOP, (POINT*)&controlRect, 2); + + if ( -1 == pt.x && -1 == pt.y ) + { + RECT itemRect; + int selected = downloadList.GetNextSelected(); + if ( selected != -1 ) // if something is selected we'll drop the menu from there + { + downloadList.GetItemRect( selected, &itemRect ); + ClientToScreen( listHwnd, (POINT *)&itemRect ); + } + else // otherwise we'll drop it from the top-left corner of the listview, adjusting for the header location + { + GetWindowRect( listHwnd, &itemRect ); + + HWND hHeader = (HWND)SNDMSG( listHwnd, LVM_GETHEADER, 0, 0L ); + RECT headerRect; + if ( ( WS_VISIBLE & GetWindowLongPtr( hHeader, GWL_STYLE ) ) && GetWindowRect( hHeader, &headerRect ) ) + { + itemRect.top += ( headerRect.bottom - headerRect.top ); + } + } + + pt.x = itemRect.left; + pt.y = itemRect.top; + } + + HWND hHeader = (HWND)SNDMSG(listHwnd, LVM_GETHEADER, 0, 0L); + if ( 0 == ( WS_VISIBLE & GetWindowLongPtr( hHeader, GWL_STYLE ) ) || FALSE == GetWindowRect( hHeader, &headerRect ) ) + { + SetRectEmpty( &headerRect ); + } + + if ( FALSE != PtInRect( &headerRect, pt ) ) + { + return; + } + + LVHITTESTINFO hitTest; + hitTest.pt = pt; + MapWindowPoints( HWND_DESKTOP, listHwnd, &hitTest.pt, 1 ); + + int index = ( downloadList.GetNextSelected() != -1 ? ListView_HitTest( listHwnd, &hitTest ) : -1 ); + + HMENU baseMenu = WASABI_API_LOADMENU( IDR_MENU1 ); + + if ( baseMenu == NULL ) + return; + + HMENU menu = GetSubMenu( baseMenu, 0 ); + if ( menu != NULL ) + { + if ( ( index == -1 ) || ( index != -1 ) && listContents[ index ]->f ) + DeleteMenu( menu, ID_DOWNLOADS_CANCELDOWNLOAD, MF_BYCOMMAND ); + + UINT enableExtras = MF_ENABLED; + if ( ( index == -1 ) || ( index != -1 ) && !listContents[ index ]->f + || ( index != -1 ) && ( listContents[ index ]->f->downloadStatus != 1 ) ) + enableExtras = ( MF_GRAYED | MF_DISABLED ); + + UINT enableViewExtras = MF_ENABLED; + if ( index == -1 ) + enableViewExtras = ( MF_GRAYED | MF_DISABLED ); + + EnableMenuItem( menu, IDC_PLAY, MF_BYCOMMAND | enableExtras ); + EnableMenuItem( menu, IDC_ENQUEUE, MF_BYCOMMAND | enableExtras ); + EnableMenuItem( menu, IDC_REMOVE, MF_BYCOMMAND | enableViewExtras ); + EnableMenuItem( menu, IDC_DELETE, MF_BYCOMMAND | enableExtras ); + EnableMenuItem( menu, IDC_INFOBOX, MF_BYCOMMAND | enableExtras ); + EnableMenuItem( menu, ID_DOWNLOADS_EXPLORERITEMFOLDER, MF_BYCOMMAND | enableExtras ); + EnableMenuItem( menu, 2, MF_BYPOSITION | enableExtras ); + + { // send-to menu shit... + ZeroMemory( &s, sizeof( s ) ); + IPC_LIBRARY_SENDTOMENU = (int)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, ( WPARAM ) & "LibrarySendToMenu", IPC_REGISTER_WINAMP_IPCMESSAGE ); + if ( IPC_LIBRARY_SENDTOMENU > 65536 && SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)0, IPC_LIBRARY_SENDTOMENU ) == 0xffffffff ) + { + s.mode = 1; + s.hwnd = hwndDlg; + s.data_type = ML_TYPE_FILENAMESW; + s.ctx[ 1 ] = 1; + s.build_hMenu = GetSubMenu( menu, 2 ); + } + } + + UpdateMenuItems( hwndDlg, menu ); + + int r = Menu_TrackPopup( plugin.hwndLibraryParent, menu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, pt.x, pt.y, hwndDlg, NULL ); + if ( !SendMessage( hwndDlg, WM_COMMAND, r, 0 ) ) + { + s.menu_id = r; // more send to menu shit... + if ( s.mode == 2 && SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU ) == 0xffffffff ) + { + s.mode = 3; + wchar_t *path = getSelectedList(); + if ( path ) + { + s.data = path; + SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU ); + free( path ); + } + } + } + + if ( s.mode ) + { // yet more send to menu shit... + s.mode = 4; + SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU ); // cleanup + } + } + + DestroyMenu( baseMenu ); +} + +static void Downloads_ContextMenu( HWND hwndDlg, WPARAM wParam, LPARAM lParam ) +{ + HWND sourceWindow = (HWND)wParam; + if ( sourceWindow == downloadList.getwnd() ) + DownloadList_RightClick( hwndDlg, sourceWindow, MAKEPOINTS( lParam ) ); +} + +enum +{ + BPM_ECHO_WM_COMMAND=0x1, // send WM_COMMAND and return value + BPM_WM_COMMAND = 0x2, // just send WM_COMMAND +}; + +BOOL Downloads_ButtonPopupMenu( HWND hwndDlg, int buttonId, HMENU menu, int flags = 0 ) +{ + RECT r; + HWND buttonHWND = GetDlgItem( hwndDlg, buttonId ); + GetWindowRect( buttonHWND, &r ); + UpdateMenuItems( hwndDlg, menu ); + MLSkinnedButton_SetDropDownState( buttonHWND, TRUE ); + UINT tpmFlags = TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_BOTTOMALIGN | TPM_LEFTALIGN; + if ( !( flags & BPM_WM_COMMAND ) ) + tpmFlags |= TPM_RETURNCMD; + int x = Menu_TrackPopup( plugin.hwndLibraryParent, menu, tpmFlags, r.left, r.top, hwndDlg, NULL ); + if ( ( flags & BPM_ECHO_WM_COMMAND ) && x ) + SendMessage( hwndDlg, WM_COMMAND, MAKEWPARAM( x, 0 ), 0 ); + MLSkinnedButton_SetDropDownState( buttonHWND, FALSE ); + return x; +} + +static void Downloads_Play( HWND hwndDlg, HWND from, UINT idFrom ) +{ + HMENU listMenu = GetSubMenu( g_context_menus2, 0 ); + int count = GetMenuItemCount( listMenu ); + if ( count > 2 ) + { + for ( int i = 2; i < count; i++ ) + { + DeleteMenu( listMenu, 2, MF_BYPOSITION ); + } + } + + Downloads_ButtonPopupMenu( hwndDlg, idFrom, listMenu, BPM_WM_COMMAND ); +} + +static BOOL WINAPI DownloadDialog_DlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITMENUPOPUP: // yet yet more send to menu shit... + if (wParam && (HMENU)wParam == s.build_hMenu && s.mode==1) + { + if (SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&s,IPC_LIBRARY_SENDTOMENU)==0xffffffff) + s.mode=2; + } + break; + + case WM_USER+543: + Navigation_Update(); + break; + + case WM_CONTEXTMENU: + Downloads_ContextMenu(hwndDlg, wParam, lParam); + return TRUE; + + case WM_NOTIFYFORMAT: + return NFR_UNICODE; + + case WM_INITDIALOG: + Downloads_Init(hwndDlg); + break; + + case WM_NOTIFY: + { + LPNMHDR l = (LPNMHDR)lParam; + if (l->idFrom == IDC_DOWNLOADLIST) + return (BOOL)DownloadList_Notify(l,hwndDlg); + } + break; + + case WM_DESTROY: + Downloads_Destroy(hwndDlg); + return 0; + + case WM_DISPLAYCHANGE: + Downloads_DisplayChange(hwndDlg); + return 0; + + case WM_TIMER: + Downloads_Timer(hwndDlg, (UINT)wParam); + break; + + case WM_MOUSEMOVE: + if (we_are_drag_and_dropping && GetCapture() == hwndDlg) + { + POINT p = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + ClientToScreen(hwndDlg, &p); + mlDropItemStruct m; + ZeroMemory(&m, sizeof(mlDropItemStruct)); + m.type = ML_TYPE_FILENAMESW; + m.p = p; + SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDRAG); + } + break; + + case WM_LBUTTONUP: + if (we_are_drag_and_dropping && GetCapture() == hwndDlg) + { + we_are_drag_and_dropping = 0; + ReleaseCapture(); + POINT p = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + ClientToScreen(hwndDlg, &p); + mlDropItemStruct m = {0}; + m.type = ML_TYPE_FILENAMESW; + m.p = p; + m.flags = ML_HANDLEDRAG_FLAG_NOCURSOR; + SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDRAG); + if (m.result > 0) + { + m.flags = 0; + m.result = 0; + wchar_t* path = getSelectedList(); + if(path) + { + m.data = path; + SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDROP); + free(path); + } + } + } + break; + + case WM_PAINT: + { + int tab[] = { IDC_DOWNLOADLIST|DCW_SUNKENBORDER}; + dialogSkinner.Draw(hwndDlg, tab, sizeof(tab) / sizeof(tab[0])); + } + return 0; + + case WM_WINDOWPOSCHANGED: + if ((SWP_NOSIZE | SWP_NOMOVE) != ((SWP_NOSIZE | SWP_NOMOVE) & ((WINDOWPOS*)lParam)->flags) || + (SWP_FRAMECHANGED & ((WINDOWPOS*)lParam)->flags)) + { + LayoutWindows(hwndDlg, !(SWP_NOREDRAW & ((WINDOWPOS*)lParam)->flags)); + } + return 0; + + case WM_USER + 0x200: + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 1); // yes, we support no - redraw resize + return TRUE; + + case WM_USER + 0x201: + offsetX = (short)LOWORD(wParam); + offsetY = (short)HIWORD(wParam); + g_rgnUpdate = (HRGN)lParam; + return TRUE; + + case WM_APP + 104: + { + Downloads_UpdateButtonText(hwndDlg, (int)wParam); + LayoutWindows(hwndDlg, TRUE); + return 0; + } + + case WM_COMMAND: + switch ( LOWORD( wParam ) ) + { + case IDC_PLAY: + case IDC_ENQUEUE: + case IDC_CUSTOM: + if ( HIWORD( wParam ) == MLBN_DROPDOWN ) + { + Downloads_Play( hwndDlg, (HWND)lParam, LOWORD( wParam ) ); + } + else + { + bool action; + if ( LOWORD( wParam ) == IDC_PLAY ) + { + action = ( HIWORD( wParam ) == 1 ) ? ML_ENQDEF_VAL() == 1 : 0; + } + else if ( LOWORD( wParam ) == IDC_ENQUEUE ) + { + action = ( HIWORD( wParam ) == 1 ) ? ML_ENQDEF_VAL() != 1 : 1; + } + else + break; + + Downloads_Play( action ); + } + break; + case IDC_REMOVE: + Downloads_Remove(); + break; + case IDC_DELETE: + Downloads_Delete( hwndDlg ); + break; + case IDC_CLEANUP: + Downloads_CleanUp( hwndDlg ); + break; + case IDC_INFOBOX: + Downloads_InfoBox( hwndDlg ); + break; + case IDC_SELECTALL: + Downloads_SelectAll(); + break; + case ID_DOWNLOADS_EXPLORERITEMFOLDER: + exploreItemFolder( hwndDlg ); + break; + case ID_DOWNLOADS_CANCELDOWNLOAD: + Downloads_Cancel(); + break; + default: + return 0; + } + return 1; + } + return 0; +} + +HWND CALLBACK DownloadDialog_Create( HWND hParent ) +{ + return WASABI_API_CREATEDIALOGPARAMW( IDD_DOWNLOADS, hParent, DownloadDialog_DlgProc, 0 ); +} \ No newline at end of file -- cgit