767 lines
26 KiB
C++
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
|
||
|
|
||
|
}
|