aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Library/ml_plg/generate.cpp
diff options
context:
space:
mode:
authorJean-Francois Mauguit <jfmauguit@mac.com>2024-09-24 09:03:25 -0400
committerGitHub <noreply@github.com>2024-09-24 09:03:25 -0400
commitbab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Library/ml_plg/generate.cpp
parent4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff)
parent20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff)
downloadwinamp-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.cpp766
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
+
+}