winamp/Src/Plugins/Library/ml_plg/generate.cpp
2024-09-24 14:54:57 +02:00

767 lines
26 KiB
C++

#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
}