aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Library/ml_plg/generate.cpp
blob: ed392dda75eed56bde18d3eb135e4c4e7e4fa86f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
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

}