diff options
author | Jean-Francois Mauguit <jfmauguit@mac.com> | 2024-09-24 09:03:25 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-24 09:03:25 -0400 |
commit | bab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Library/ml_plg/generate.cpp | |
parent | 4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff) | |
parent | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff) | |
download | winamp-bab614c421ed7ae329d26bf028c4a3b1d2450f5a.tar.gz |
Merge pull request #5 from WinampDesktop/community
Merge to main
Diffstat (limited to 'Src/Plugins/Library/ml_plg/generate.cpp')
-rw-r--r-- | Src/Plugins/Library/ml_plg/generate.cpp | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_plg/generate.cpp b/Src/Plugins/Library/ml_plg/generate.cpp new file mode 100644 index 00000000..ed392dda --- /dev/null +++ b/Src/Plugins/Library/ml_plg/generate.cpp @@ -0,0 +1,766 @@ +#include "../gracenote/gracenote.h" +#include "api__ml_plg.h" +#include <windows.h> +#include "resource.h" +#include "../../General/gen_ml/ml.h" +#include "../winamp/wa_ipc.h" +#include "../Agave/Language/api_language.h" +#include "../nu/MediaLibraryInterface.h" +#include "../nu/ComboBox.h" + +#include "main.h" +#include <shlwapi.h> +#include <assert.h> +#include "playlist.h" +#include <atlbase.h> +#include "IDScanner.h" +//#include "../Wasabi/bfc/util/timefmt.h" +//#include <bfc/util/timefmt.h> + +#include <strsafe.h> // should be last + +HWND hwndDlgCurrent = 0; +bool optionsVisible = true; +bool isGenerating = false; +int originalWidth = 877; +//#define DIALOG_WIDTH_OPTIONS 877 // use originalWidth instead +#define DIALOG_WIDTH_NO_OPTIONS 610 +#define DIALOG_HIDDEN_COLUMN_ID 4 + +// Pass in 0 for width or height in order to preserve its current dimension +void SizeWindow(HWND hwnd, int width, int height) +{ + if (width == 0 || height == 0) // Preserve only if one of the items is 0 + { + RECT windowRect; + GetWindowRect(hwnd, &windowRect); + + if (width == 0) // Preserve the width + width = windowRect.right - windowRect.left; + if (height == 0) // Preserve the height + height = windowRect.bottom - windowRect.top; + } + SetWindowPos(hwnd, NULL, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); +} + +void ClientResize(HWND hWnd, int nWidth, int nHeight) +{ + RECT rcClient, rcWind; + POINT ptDiff; + GetClientRect(hWnd, &rcClient); + GetWindowRect(hWnd, &rcWind); + ptDiff.x = (rcWind.right - rcWind.left) - rcClient.right; + ptDiff.y = (rcWind.bottom - rcWind.top) - rcClient.bottom; + MoveWindow(hWnd,rcWind.left, rcWind.top, nWidth + ptDiff.x, nHeight + ptDiff.y, TRUE); +} + +void SetMarqueeProgress(bool isMarquee) +{ + HWND hwndProgress = GetDlgItem(hwndDlgCurrent,IDC_PROGRESS_GENERATE); + static long state = GetWindowLongW(hwndProgress, GWL_STYLE); // Capture the initial state of the progress bar + + + if (isMarquee) // Set it to marquee style + { + SetWindowLong (hwndProgress, GWL_STYLE, GetWindowLong(hwndProgress, GWL_STYLE) | PBS_MARQUEE); + //SendMessage(hwndProgress, PBM_SETMARQUEE, 1, 10); + SendMessage(hwndProgress, PBM_SETMARQUEE, 1, 30); + } + else // Restore the normal progress bar + { + SetWindowLong (hwndProgress, GWL_STYLE, state); + //SendMessage(hwndProgress, WM_PAINT, 0, 0); + InvalidateRect(hwndProgress, 0, 1); // Force a repaint of the marquee after turning it off because there are stuck pixels in XP + } +} + +// Sets the query check state as well as enabling all the controls involved +void SetMLQueryCheckState(HWND hwndDlg, unsigned int checked) +{ + // Get the handles to all the child controls we want to enable / disable + HWND hwndButtonMlQuery = GetDlgItem(hwndDlg, IDC_BUTTON_ML_QUERY); + HWND hwndEditMlQuery = GetDlgItem(hwndDlg, IDC_EDIT_ML_QUERY); + HWND hwndButtonRestoreQueryDefault = GetDlgItem(hwndDlg, IDC_BUTTON_RESTORE_QUERY_DEFAULT); + + if (checked) // enable all the controls related to ML query + { + CheckDlgButton(hwndDlg, IDC_CHECK_ML_QUERY, TRUE); + EnableWindow (hwndButtonMlQuery, TRUE ); + EnableWindow (hwndEditMlQuery, TRUE ); + EnableWindow (hwndButtonRestoreQueryDefault, TRUE ); + useMLQuery = true; + } + else // disable all the controls related to ML query + { + CheckDlgButton(hwndDlg, IDC_CHECK_ML_QUERY, FALSE); + EnableWindow (hwndButtonMlQuery, FALSE ); + EnableWindow (hwndEditMlQuery, FALSE ); + EnableWindow (hwndButtonRestoreQueryDefault, FALSE ); + useMLQuery = false; + } +} + +void SetButtonsEnabledState(bool enabled_flag) +{ + int itemIds[] = + { + IDC_BUTTON_PLAY_NOW, + IDC_BUTTON_ENQUEUE_NOW, + IDC_BUTTON_SAVEAS, + IDC_BUTTON_REGENERATE + }; + + for(int i = 0; i < sizeof(itemIds) / sizeof(itemIds[0]); i++) + EnableWindow(GetDlgItem(hwndDlgCurrent, itemIds[i]), enabled_flag); +} + +void ToggleOptions(bool reset) +{ + if (reset) + optionsVisible = false; + else + optionsVisible = !optionsVisible; // Toggle the options visible state + + // to resolve tabbing issues when in the collapsed + // state we need to disable some of the controls (dro) + int itemIds[] = { + IDC_RADIO_PLAYLIST_ITEMS, + IDC_RADIO_PLAYLIST_LENGTH, + IDC_RADIO_PLAYLIST_SIZE, + IDC_COMBO_LENGTH, + IDC_CHECK_USE_SEED, + IDC_CHECK_MULTIPLE_ARTISTS, + IDC_CHECK_MULTIPLE_ALBUMS, + IDC_CHECK_ML_QUERY, + IDC_EDIT_ML_QUERY, + IDC_BUTTON_ML_QUERY, + IDC_BUTTON_RESTORE_QUERY_DEFAULT + }; + for(int i = 0; i < sizeof(itemIds) / sizeof(itemIds[0]); i++) + EnableWindow(GetDlgItem(hwndDlgCurrent, itemIds[i]), optionsVisible); + + SetMLQueryCheckState(hwndDlgCurrent, useMLQuery); + + if (optionsVisible) + { + SizeWindow(hwndDlgCurrent, originalWidth, 0); // Resize the window to the correct width + SetDlgItemText(hwndDlgCurrent, IDC_BUTTON_OPTIONS, WASABI_API_LNGSTRINGW(IDS_OPTIONS)); // Set the dialog button to show the correct options mode + } + else + { + SizeWindow(hwndDlgCurrent, DIALOG_WIDTH_NO_OPTIONS, 0); + SetDlgItemText(hwndDlgCurrent, IDC_BUTTON_OPTIONS, WASABI_API_LNGSTRINGW(IDS_NO_OPTIONS)); // Set the dialog button to show the correct options mode + } +} + +// ToDo: Make this more human readable +void FormatToMinutesAndSeconds(const int lengthInSeconds, wchar_t *buff, const size_t cchBuf) +{ + //StringCchPrintfW(buff, cchBuf, L"%d:%02d", lengthInSeconds / 60, lengthInSeconds % 60); + + int total_length_s = lengthInSeconds; + int uncert = 0; + + // Minutes and seconds + if (total_length_s < 60*60) StringCchPrintfW(buff, 64, L"%s%u:%02u", uncert ? L"~" : L"", total_length_s / 60, total_length_s % 60); + // Hours minutes and seconds + else if (total_length_s < 60*60*24) StringCchPrintfW(buff, 64, L"%s%u:%02u:%02u", uncert ? L"~" : L"", total_length_s / 60 / 60, (total_length_s / 60) % 60, total_length_s % 60); + else + { + wchar_t days[16] = {0}; + int total_days = total_length_s / (60 * 60 * 24); // Calculate days + total_length_s -= total_days * 60 * 60 * 24; // Remove days from length + StringCchPrintfW(buff, 64, + //WASABI_API_LNGSTRINGW(IDS_LENGTH_DURATION_STRING), + L"%s%u %s+%u:%02u:%02u", + ((uncert) ? L"~" : L""), total_days, // Approximate + WASABI_API_LNGSTRINGW_BUF(total_days == 1 ? IDS_DAY : IDS_DAYS, days, 16), // Days + total_length_s / 60 / 60, // Hours + (total_length_s / 60) % 60, // Minutes + total_length_s % 60); // Seconds + } +} + +// Refreashed the statistics about the generated playlist +void UpdateStats(void) +{ + const int MAX_STATS = 512; + wchar_t stats[MAX_STATS] = {0}; + wchar_t lengthText[MAX_STATS] = {0}; + wchar_t sizeText[MAX_STATS] = {0}; + int count = (int)currentPlaylist.GetNumItems(); + uint64_t length = currentPlaylist.GetPlaylistLengthMilliseconds(); + uint64_t size = currentPlaylist.GetPlaylistSizeBytes(); + + // Add the seed stats? + if (useSeed == TRUE) + { + count += (int)seedPlaylist.GetNumItems(); + length += seedPlaylist.GetPlaylistLengthMilliseconds(); + size += seedPlaylist.GetPlaylistSizeBytes(); + } + + FormatToMinutesAndSeconds((int)(length / 1000), lengthText, MAX_STATS); // / 1000 because we have it in milliseconds and not seconds + StrFormatByteSizeW(size, sizeText, MAX_STATS); // Get the human readable formatting for filesize + + StringCchPrintf(stats, MAX_STATS, WASABI_API_LNGSTRINGW(IDS_STATS), count, lengthText, sizeText); + + SetDlgItemText(hwndDlgCurrent, IDC_STATIC_STATS, stats); // Set the dialog button to show the correct options mode +} + +// Update the progress to the current +static void doProgressBar(HWND h, int x, int t=-1) { + h = GetDlgItem(h,IDC_PROGRESS_GENERATE); + if(t!=-1 && SendMessage(h,PBM_GETRANGE,0,0) != t) + SendMessage(h,PBM_SETRANGE32,0,t); + SendMessage(h,PBM_SETPOS,x,0); +} + +// Update the status while id scanner is active +static void FillStatus(HWND hwndDlg) +{ + long state, track, tracks; + if (scanner.GetStatus(&state, &track, &tracks)) + { + static int x=0; + wchar_t *ticker; + switch (x++) + { + case 0: ticker=L""; break; + case 1: ticker=L"."; break; + case 2: ticker=L".."; break; + default: ticker=L"..."; + } + x%=4; + wchar_t status[1024]=L""; + switch (state) + { + case IDScanner::STATE_ERROR: + WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_INITIALIZING,status,1024); + KillTimer(hwndDlg, 1); + doProgressBar(hwndDlg,0,1); + ShowErrorDlg(hwndDlg); + break; + case IDScanner::STATE_IDLE: + WASABI_API_LNGSTRINGW_BUF(IDS_IDLE,status,1024); + doProgressBar(hwndDlg,0); + break; + case IDScanner::STATE_INITIALIZING: + StringCchPrintfW(status, 1024, WASABI_API_LNGSTRINGW(IDS_INITIALIZING), ticker); + doProgressBar(hwndDlg,0); + break; + case IDScanner::STATE_SYNC: + StringCchPrintfW(status, 1024, WASABI_API_LNGSTRINGW(IDS_SYNC), track, tracks, ticker); + doProgressBar(hwndDlg,track,tracks); + break; + case IDScanner::STATE_METADATA: + StringCchPrintfW(status, 1024, WASABI_API_LNGSTRINGW(IDS_METADATA), track, tracks, ticker); + doProgressBar(hwndDlg,track,tracks); + break; + case IDScanner::STATE_MUSICID: + StringCchPrintfW(status, 1024, WASABI_API_LNGSTRINGW(IDS_MUSICID), track, tracks, ticker); + doProgressBar(hwndDlg,track,tracks); + break; + case IDScanner::STATE_DONE: + if (!isGenerating) // Only set the done state if the gneeration has not started yet + { + WASABI_API_LNGSTRINGW_BUF(IDS_DONE,status,1024); + doProgressBar(hwndDlg,0,0); // Turn off the progress bar to 0 + } + + KillTimer(hwndDlg, 1); + break; + } + if (!isGenerating) // Only set the done state if the gneeration has not started yet + { + SetDlgItemTextW(hwndDlg, IDC_STATIC_PROGRESS_STATE, status); + } + } +} + +// Function calls appropriate items when a generation is requested +void Regenerate(HWND hwndDlg) +{ + SendMessage(GetDlgItem(hwndDlgCurrent, IDC_LIST_RESULTS2),LVM_DELETEALLITEMS,0,0); // Clear the listview of all playlist items + + SetTimer(hwndDlg, 1, 500, 0); // Set the progress timer for the scanner + StartScan(); + + MoreLikeTheseSongs(&seedPlaylist); +} + +// Function draws in colors for the seed listview items +LRESULT CustomDrawListViewColors(LPARAM lParam) +{ + LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam; + + switch(lplvcd->nmcd.dwDrawStage) + { + case CDDS_PREPAINT : //Before the paint cycle begins + return CDRF_NOTIFYITEMDRAW; //request notifications for individual listview items + + case CDDS_ITEMPREPAINT: //Before an item is drawn + if (lplvcd->nmcd.dwItemSpec < seedPlaylist.entries.size()) // Check how many seeds we have, thats how we know which rows in the view to color paint + { + if (useSeed == TRUE) + { + lplvcd->clrText = RGB(0,0,255); // Color seed tracks blue + } + else + { + lplvcd->clrText = RGB(100,100,100); // Color seed tracks a faded grey + } + } + return CDRF_NEWFONT; + break; + } + return CDRF_DODEFAULT; +} + +int SetRadioControlsState(HWND hwndDlg) +{ + // Set the radio buttons for playlist length type, items or minutes + if(plLengthType == PL_ITEMS) + { + CheckDlgButton(hwndDlg,IDC_RADIO_PLAYLIST_ITEMS,TRUE); + SendMessage(hwndDlg, WM_COMMAND, IDC_RADIO_PLAYLIST_ITEMS, 0); + SetPlLengthTypeComboToItems(hwndDlg, plItems); + } + else if(plLengthType == PL_MINUTES) + { + CheckDlgButton(hwndDlg,IDC_RADIO_PLAYLIST_LENGTH,TRUE); + SendMessage(hwndDlg, WM_COMMAND, IDC_RADIO_PLAYLIST_LENGTH, 0); + SetPlLengthTypeComboToMinutes(hwndDlg, plMinutes); + } + else if(plLengthType == PL_MEGABYTES) + { + CheckDlgButton(hwndDlg,IDC_RADIO_PLAYLIST_SIZE,TRUE); + SendMessage(hwndDlg, WM_COMMAND, IDC_RADIO_PLAYLIST_SIZE, 0); + SetPlLengthTypeComboToMegabytes(hwndDlg, plMegabytes); + } + + return 0; +} + +// Update the combo box contents depending on which lengthType we are using +int UpdateComboLength(HWND hwndDlg) +{ + const int BUF_SIZE = 32; + ComboBox combo(hwndDlg, IDC_COMBO_LENGTH); + wchar_t buf[BUF_SIZE] = {0}; + combo.GetEditText(buf, BUF_SIZE); + + switch(plLengthType) + { + case PL_ITEMS: + plItems = _wtoi(buf); + return 0; + break; + case PL_MINUTES: + plMinutes = _wtoi(buf); + return 0; + break; + case PL_MEGABYTES: + plMegabytes = _wtoi(buf); + return 0; + break; + } + return 1; +} + +LRESULT tab_fix_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if(uMsg == WM_CHAR) + { + if(wParam == VK_TAB) + { + SendMessage(hwndDlgCurrent, WM_NEXTDLGCTL, (GetAsyncKeyState(VK_SHIFT)&0x8000), FALSE); + return TRUE; + } + } + + return CallWindowProcW((WNDPROC)GetPropW(hwndDlg, L"tab_fix_proc"), hwndDlg, uMsg, wParam, lParam); +} + +// this will prevent the hidden column (for making the headers work better) +// from appearing as sizeable / disabled (as it effectively is) +LRESULT header_block_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if(uMsg == WM_SETCURSOR) + { + HDHITTESTINFO hitTest; + GetCursorPos(&hitTest.pt); + ScreenToClient(hwndDlg, &hitTest.pt); + hitTest.flags = hitTest.iItem = 0; + SendMessage(hwndDlg, HDM_HITTEST, FALSE, (LPARAM)&hitTest); + if(hitTest.iItem == DIALOG_HIDDEN_COLUMN_ID || hitTest.iItem == -1) + { + SetCursor(LoadCursor(NULL, IDC_ARROW)); + return TRUE; + } + } + + return CallWindowProcW((WNDPROC)GetPropW(hwndDlg, L"header_block_proc"), hwndDlg, uMsg, wParam, lParam); +} + +INT_PTR CALLBACK GenerateProcedure(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + RECT r; + GetWindowRect(hwndDlg, &r); + originalWidth = r.right - r.left; + + // bit hacky but it will resolve issues with tabbing and the combobox in a + // dropdown style still not 100% sure why it's failing to work though (dro) + HWND combobox = GetWindow(GetDlgItem(hwndDlg, IDC_COMBO_LENGTH), GW_CHILD); + SetPropW(combobox, L"tab_fix_proc",(HANDLE)SetWindowLongPtrW(combobox, GWLP_WNDPROC, (LONG_PTR)tab_fix_proc)); + + hwndDlgCurrent = hwndDlg; // Set the global so that we have a window open + + // this will make sure that we've got thr aacplus logo shown even when using a localised version + SendDlgItemMessage(hwndDlg,IDC_LOGO,STM_SETIMAGE,IMAGE_BITMAP, + (LPARAM)LoadImage(plugin.hDllInstance,MAKEINTRESOURCE(IDB_GN_LOGO),IMAGE_BITMAP,0,0,LR_SHARED)); + + BoldStatusText(GetDlgItem(hwndDlg, IDC_STATIC_PROGRESS_STATE) ); + + SetRadioControlsState(hwndDlg); // Set the playlist length state + + if(multipleArtists) + CheckDlgButton(hwndDlg,IDC_CHECK_MULTIPLE_ARTISTS,TRUE); + if(multipleAlbums) + CheckDlgButton(hwndDlg,IDC_CHECK_MULTIPLE_ALBUMS,TRUE); + if(useSeed) + CheckDlgButton(hwndDlg,IDC_CHECK_USE_SEED,TRUE); + + // Set up the colums for the playlist listing + #define ListView_InsertColumnW(hwnd, iCol, pcol) \ + (int)SNDMSG((hwnd), LVM_INSERTCOLUMNW, (WPARAM)(int)(iCol), (LPARAM)(const LV_COLUMNW *)(pcol)) + //SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam); + + // Add the columns to the listbox + HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST_RESULTS2); + ListView_SetExtendedListViewStyle(hwndlist, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP); + LVCOLUMNW lvc = {0, }; + lvc.mask = LVCF_TEXT|LVCF_WIDTH; + lvc.pszText = WASABI_API_LNGSTRINGW(IDS_TITLE); // Initialize the columns of the listview + lvc.cx = 160; + ListView_InsertColumnW(hwndlist, 0, &lvc); + lvc.pszText = WASABI_API_LNGSTRINGW(IDS_LENGTH); + lvc.cx = 80; + ListView_InsertColumnW(hwndlist, 1, &lvc); + lvc.pszText = WASABI_API_LNGSTRINGW(IDS_SIZE); + lvc.cx = 80; + ListView_InsertColumnW(hwndlist, 2, &lvc); + lvc.pszText = WASABI_API_LNGSTRINGW(IDS_SEED); + lvc.cx = 80; + ListView_InsertColumnW(hwndlist, 3, &lvc); + lvc.pszText = 0; + lvc.cx = 0; + ListView_InsertColumnW(hwndlist, DIALOG_HIDDEN_COLUMN_ID, &lvc); + + // Autosize the columns taking the header into consideration + ListView_SetColumnWidth(hwndlist,0,LVSCW_AUTOSIZE_USEHEADER); + ListView_SetColumnWidth(hwndlist,1,LVSCW_AUTOSIZE_USEHEADER); + ListView_SetColumnWidth(hwndlist,2,LVSCW_AUTOSIZE_USEHEADER); + ListView_SetColumnWidth(hwndlist,3,LVSCW_AUTOSIZE_USEHEADER); + + HWND hwndListHeader = ListView_GetHeader(hwndlist); + SetPropW(hwndListHeader, L"header_block_proc",(HANDLE)SetWindowLongPtrW(hwndListHeader, GWLP_WNDPROC, (LONG_PTR)header_block_proc)); + + // Background color for highlighting seed tracks. + //hbrBkcolor = CreateSolidBrush ( RGB(255,0,0) ); + + BoldStatusText(GetDlgItem(hwndDlg, IDC_STATIC_STATS) ); + + // Populate the query textbox + SetDlgItemTextW(hwndDlg, IDC_EDIT_ML_QUERY, mlQuery); // Set the text for the query + + // Disable the regenerate button because we will be scanning the library and generating on initialization + //EnableWindow (GetDlgItem(hwndDlgCurrent, IDC_BUTTON_REGENERATE), FALSE ); // This is for initialization + SetButtonsEnabledState(false); + + // Set up the window with the options hidden + ToggleOptions(true); + + // Show the window since we are modeless + POINT pt = {(LONG)GetPrivateProfileInt(L"ml_plg", L"generate_x",-1, mediaLibrary.GetWinampIniW()), + (LONG)GetPrivateProfileInt(L"ml_plg", L"generate_y",-1, mediaLibrary.GetWinampIniW())}; + if (!windowOffScreen(hwndDlg, pt)) + SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING); + else + ShowWindow(hwndDlg, SW_SHOW); + + Regenerate(hwndDlg); + + if (WASABI_API_APP) // Add direct mousewheel support for the main tracklist view of seed and generated tracks + WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(GetDlgItem(hwndDlg, IDC_LIST_RESULTS2), TRUE); + + return TRUE; + } + break; + + // Trying to change the background color of seed tracks here + /*case WM_CTLCOLORSTATIC: + { + HDC hdc = (HDC) wParam; + HWND hwndStatic = (HWND) lParam; + + if ( hwndStatic == GetDlgItem ( hwndDlg, IDC_LIST_RESULTS2 )) + { + SetBkMode ( hdc, TRANSPARENT ); + return (LRESULT) hbrBkcolor; + } + } + break;*/ + + case WM_TIMER: + FillStatus(hwndDlg); + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDCANCEL: + { + RECT rect = {0}; + GetWindowRect(hwndDlg, &rect); + char buf[16] = {0}; + StringCchPrintfA(buf, 16, "%d", rect.left); + WritePrivateProfileStringA("ml_plg", "generate_x", buf, mediaLibrary.GetWinampIni()); + StringCchPrintfA(buf, 16, "%d", rect.top); + WritePrivateProfileStringA("ml_plg", "generate_y", buf, mediaLibrary.GetWinampIni()); + + EndDialog(hwndDlg, 0); + hwndDlgCurrent = 0; // Set to null so new instance can be opened + + WriteSettingsToIni(hwndDlg); + + // We need to free up our seed tracks because we no longer require them + seedPlaylist.Clear(); // Clear the global seed list + } + break; + case IDC_BUTTON_CANCEL: + SendMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0); + break; + case IDC_BUTTON_REGENERATE: + Regenerate(hwndDlg); + break; + case IDC_RADIO_PLAYLIST_ITEMS: + SetDlgItemText(hwndDlg, IDC_LENGTH_TYPE, WASABI_API_LNGSTRINGW(IDS_ITEMS)); + plLengthType = PL_ITEMS; // Set to # of items + SetPlLengthTypeComboToItems(hwndDlg, plItems); + break; + case IDC_RADIO_PLAYLIST_LENGTH: + SetDlgItemText(hwndDlg, IDC_LENGTH_TYPE, WASABI_API_LNGSTRINGW(IDS_MINUTES)); + plLengthType = PL_MINUTES; // Set to minutes + SetPlLengthTypeComboToMinutes(hwndDlg, plMinutes); + break; + case IDC_RADIO_PLAYLIST_SIZE: + SetDlgItemText(hwndDlg, IDC_LENGTH_TYPE, WASABI_API_LNGSTRINGW(IDS_MEGABYTES)); + plLengthType = PL_MEGABYTES; // Set to megabytes + SetPlLengthTypeComboToMegabytes(hwndDlg, plMegabytes); + break; + case IDC_COMBO_LENGTH: + { + UpdateComboLength(hwndDlg); + } + break; + case IDC_BUTTON_OPTIONS: + ToggleOptions(false); + break; + case IDC_BUTTON_PLAY_NOW: + playPlaylist(currentPlaylist, false, 0, /*seed,*/ useSeed); // Play the current playlist taking the seed track into consideration + SendMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0); // Close up the dialog because we are done + break; + case IDC_BUTTON_ENQUEUE_NOW: + playPlaylist(currentPlaylist, true, 0, /*seed,*/ useSeed); // Enqueue the current playlist taking the seed track into consideration + SendMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0); // Close up the dialog because we are done + break; + case IDC_BUTTON_SAVEAS: + { + // ToDo spawn a dialog to save the current playlist as a ML playlist + int save_result = WASABI_API_DIALOGBOXPARAM(IDD_ADD_PLAYLIST, hwndDlg, AddPlaylistDialogProc, (LPARAM)&seedPlaylist/*seed*/); + if (save_result == IDOK) // If the user accepted that playlist dialog then go ahead and close up everything + { + SendMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0); // Close up the dialog because we are done + } + } + break; + case IDC_BUTTON_ML_QUERY: + { + char temp[1024] = {0}; + GetDlgItemTextA(hwndDlg, IDC_EDIT_ML_QUERY, temp, sizeof(temp) - 1); // Retreive the current custom ML query + + ml_editview meq = {hwndDlg, (temp[0] == 0) ? DEFAULT_ML_QUERY : temp, "ML Query", -1}; // Create the editview + meq.name = WASABI_API_LNGSTRING(IDS_ML_QUERY); // Set a custom title + if(!(int)SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (LPARAM)&meq, ML_IPC_EDITVIEW)) + return 0; // Spawn the edit view + SetDlgItemTextA(hwndDlg, IDC_EDIT_ML_QUERY, meq.query); // Set the text back to the edited query + } + break; + case IDC_BUTTON_RESTORE_QUERY_DEFAULT: + SetDlgItemTextW(hwndDlg, IDC_EDIT_ML_QUERY, _T(DEFAULT_ML_QUERY)); // Set the text back to the edited query + break; + case IDC_CHECK_USE_SEED: + useSeed = IsDlgButtonChecked(hwndDlg,IDC_CHECK_USE_SEED); + UpdateStats(); // Update the track stats, because the seed status can change them + RedrawWindow(GetDlgItem(hwndDlg,IDC_LIST_RESULTS2), 0, 0, RDW_INVALIDATE); // Refresh the colors in the list view + break; + case IDC_CHECK_MULTIPLE_ARTISTS: // Set the multiple tracks per artist option when checked + multipleArtists = IsDlgButtonChecked(hwndDlg, IDC_CHECK_MULTIPLE_ARTISTS); + break; + case IDC_CHECK_MULTIPLE_ALBUMS: // Set the multiple tracks per album option when checked + multipleAlbums = IsDlgButtonChecked(hwndDlg, IDC_CHECK_MULTIPLE_ALBUMS); + break; + case IDC_CHECK_ML_QUERY: + SetMLQueryCheckState(hwndDlg, IsDlgButtonChecked(hwndDlg, IDC_CHECK_ML_QUERY)); + break; + case IDC_EDIT_ML_QUERY: + if (HIWORD(wParam) == EN_CHANGE) + { + GetDlgItemTextW(hwndDlg, IDC_EDIT_ML_QUERY, mlQuery, MAX_ML_QUERY_SIZE); // Set the text back to the edited query + break; + } + } + break; + + case WM_NOTIFY: + if(((LPNMHDR)lParam)->code == HDN_BEGINTRACKW || ((LPNMHDR)lParam)->code == HDN_BEGINTRACKA || + ((LPNMHDR)lParam)->code == HDN_ITEMCHANGINGW || ((LPNMHDR)lParam)->code == HDN_ITEMCHANGINGA) + { + LPNMHEADER pNMHeader = (LPNMHEADER)lParam; + if(pNMHeader->iItem == DIALOG_HIDDEN_COLUMN_ID) + { + SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)TRUE); + return TRUE; + } + } + else if(((LPNMHDR)lParam)->code == NM_CUSTOMDRAW) // Notify for List View custom redraw (seed track colors) + { +#if defined(_WIN64) + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG)CustomDrawListViewColors(lParam)); +#else + SetWindowLong(hwndDlg, DWL_MSGRESULT, (LONG)CustomDrawListViewColors(lParam)); +#endif + return TRUE; + } + + { + const int controls[] = + { + IDC_LIST_RESULTS2, + }; + if (WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(hwndDlg, msg, wParam, lParam, controls, ARRAYSIZE(controls)) != FALSE) + { + return TRUE; + } + } + break; + case WM_DESTROY: + { + if (WASABI_API_APP) + WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(GetDlgItem(hwndDlg, IDC_LIST_RESULTS2), FALSE); + } + break; + } + + return 0; +} + +int AddResultListItem(Playlist *playlist, int index, int position, bool seed) +{ + const unsigned int MAX_INFO = 256; + wchar_t filename[MAX_INFO] = {0}; + wchar_t info[MAX_INFO] = {0}; + wchar_t *seedText = 0; + LVITEMW lvi={LVIF_TEXT, position, 0}; + + playlist->GetItem(index,filename,MAX_INFO); + + // Add the title column + playlist->GetItemTitle(index, info, MAX_INFO); + lvi.pszText=info; + lvi.cchTextMax=sizeof(info) / sizeof(*info); + SendMessage(GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2),LVM_INSERTITEMW,0,(LPARAM)&lvi); + + // Add the length column + int length = playlist->GetItemLengthMilliseconds(index); + if (length <= 0) + StringCchCopyW(info, MAX_INFO, WASABI_API_LNGSTRINGW(IDS_UNKNOWN)); + else + FormatToMinutesAndSeconds(length / 1000, info, MAX_INFO); // / 1000 because we have it in milliseconds and not seconds + + lvi.pszText=info; + lvi.cchTextMax=sizeof(info) / sizeof(*info); + lvi.iSubItem = 1; + SendMessage(GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2),LVM_SETITEMW,0,(LPARAM)&lvi); + + // Add the size column + int size = playlist->GetItemSizeBytes(index); + if (size <= 0) + StringCchCopyW(info, MAX_INFO, WASABI_API_LNGSTRINGW(IDS_UNKNOWN)); + else + StrFormatByteSizeW(size, info, MAX_INFO); + lvi.pszText=info; + lvi.cchTextMax=sizeof(info) / sizeof(*info); + lvi.iSubItem = 2; + SendMessage(GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2),LVM_SETITEMW,0,(LPARAM)&lvi); + + // Add the seed track column + if (seed == true) + seedText = WASABI_API_LNGSTRINGW(IDS_YES); + else + seedText = WASABI_API_LNGSTRINGW(IDS_NO); + lvi.pszText=seedText; + lvi.cchTextMax=sizeof(seedText) / sizeof(*seedText); + lvi.iSubItem = 3; + SendMessage(GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2),LVM_SETITEMW,0,(LPARAM)&lvi); + + return 0; +} + +void CantPopulateResults(void) +{ + wchar_t message[256] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_EXCUSE_ME, message, 256); + MessageBoxW(hwndDlgCurrent, message, WASABI_API_LNGSTRINGW(IDS_NULLSOFT_PLAYLIST_GENERATOR), MB_OK | MB_ICONINFORMATION); +} + +void PopulateResults(Playlist *playlist) +{ + // Add all of the seed tracks to the listview + int listLength = (playlist) ? (int)playlist->GetNumItems() : 0; + int seedLength = (int)seedPlaylist.GetNumItems(); + for (int i = 0; i < seedLength; i++) + { + AddResultListItem(&seedPlaylist, i, i, true); + } + + // Add all of the generated tracks to the listview + for (int i = 0; i < listLength; i++) + { + AddResultListItem(playlist, i, seedLength + i, false); + } + + // After we are done populating the data then we can size the columns accordingly + HWND hwndlist = GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2); + ListView_SetColumnWidth(hwndlist,0,(listLength ? LVSCW_AUTOSIZE : LVSCW_AUTOSIZE_USEHEADER)); + ListView_SetColumnWidth(hwndlist,1,LVSCW_AUTOSIZE_USEHEADER); + ListView_SetColumnWidth(hwndlist,2,LVSCW_AUTOSIZE_USEHEADER); + ListView_SetColumnWidth(hwndlist,3,LVSCW_AUTOSIZE_USEHEADER); + + // Refresh the playlist stats + UpdateStats(); + + // Change the progress status to read done 'generated' + SetDlgItemText(hwndDlgCurrent,IDC_STATIC_PROGRESS_STATE, WASABI_API_LNGSTRINGW(IDS_DONE)); + + SetMarqueeProgress(false); // Turn the marquee off because we are actually generating the tracks + + //EnableWindow (GetDlgItem(hwndDlgCurrent, IDC_BUTTON_REGENERATE), TRUE ); + SetButtonsEnabledState(true); // Renable the buttons + +} |