| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071 | #include "main.h"#include "Metadata.h"#include "../Winamp/wa_ipc.h"// ID3v2 stuff#include "../id3v2/id3_tag.h"#include "FactoryHelper.h"#include "id3.h"#include "../nu/AutoWide.h"#include "../nu/AutoChar.h"#include "AACFrame.h"#include "LAMEinfo.h"#include <shlwapi.h>#include "../nu/ns_wc.h"#include "../nu/ListView.h"#include "resource.h"#include "Stopper.h"#include "config.h"#include <strsafe.h>// TODO: benski> CUT!!!char g_stream_title[256] = {0};int fixAACCBRbitrate(int br){	static short brs[] =	{		8, 12, 16, 20, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0	};	int x;	for (x = 0; x < sizeof(brs) / sizeof(brs[0]); x ++)	{		int delta = (brs[x] * 8) / 128;		if (delta < 2) delta = 2;		if (br < brs[x] - delta) break;		if (br < brs[x] + delta) return brs[x];	}	return br;}void ConvertTryUTF8(const char *in, wchar_t *out, size_t outlen){	out[0]=0;	int x = MultiByteToWideCharSZ(CP_UTF8, MB_ERR_INVALID_CHARS, in, -1, out, (int)outlen);	if (!x)	{		if (GetLastError() == ERROR_NO_UNICODE_TRANSLATION)			MultiByteToWideCharSZ(CP_ACP, 0, in, -1, out, (int)outlen);		else			MultiByteToWideCharSZ(CP_UTF8, 0, in, -1, out, (int)outlen);	}}void getfileinfo(const wchar_t *filename, wchar_t *title, int *length_in_ms){	const wchar_t *fn;	if (length_in_ms) *length_in_ms = -1000;	if (filename && filename[0])		fn = filename;	else		fn = lastfn;	if (!_wcsnicmp(fn, L"file://", 7)) fn += 7;	if (PathIsURL(fn))	{		if (title)		{			if (fn != filename || !_wcsicmp(fn, lastfn))			{				EnterCriticalSection(&g_lfnscs);				if (lastfn_status[0])				{					char buf[4096] = {0};					StringCchPrintfA(buf, 4096, "[%s] %s", lastfn_status, lastfn_data_ready ? g_stream_title : (char *)AutoChar(fn));					ConvertTryUTF8(buf, title, 256);				}				else				{					if (!lastfn_data_ready)						lstrcpynW(title, fn, 256);					else					{						ConvertTryUTF8(g_stream_title, title, 256);					}				}				LeaveCriticalSection(&g_lfnscs);				if (length_in_ms) *length_in_ms = getlength();			}			else			{				lstrcpynW(title, fn, 256);			}		}		return ;	}	else	{		Metadata info;		if (info.Open(fn) == METADATA_SUCCESS)		{			if (title)			{				wchar_t mp3artist[256] = L"", mp3title[256] = L"";				info.GetExtendedData("artist", mp3artist, 256);				info.GetExtendedData("title", mp3title, 256);				if (mp3artist[0] && mp3title[0])					StringCchPrintfW(title, 256, L"%s - %s", mp3artist, mp3title);				else if (mp3title[0])					lstrcpynW(title, mp3title, 256);				else				{					lstrcpynW(title, fn, MAX_PATH);					PathStripPathW(title);					PathRemoveExtensionW(title);				}			}			if (fn == filename)			{				wchar_t ln[128]=L"";				info.GetExtendedData("length", ln, 128);				*length_in_ms = _wtoi(ln);			}			else				*length_in_ms = getlength();		}		else if (fn != filename)			*length_in_ms = getlength();	}}int id3Dlg(const wchar_t *fn, HWND hwnd){	return 1;}extern const wchar_t *id3v1_genres[];extern size_t numGenres;static int our_change=0;#define GetMeta(hwnd) (Metadata *)GetPropW(GetParent(hwnd),L"mp3info")#define SetMeta(hwnd,meta) SetPropW(GetParent(hwnd),L"mp3info",(HANDLE)meta)static INT_PTR CALLBACK id3v1_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){	static int my_change_v1=0;	static const int ctrls[] =	{		IDC_ID3V11_TRACK,		IDC_ID3_TITLE,		IDC_ID3_ARTIST,		IDC_ID3_ALBUM,		IDC_ID3_YEAR,		IDC_ID3_COMMENT,		IDC_ID3_GENRE,	};	static const int strs_lim[] =	{		3,		30,		30,		30,		4,		28,		1,	};	static const char * strs[] =	{		"track",		"title",		"artist",		"album",		"year",		"comment",		"genre",	};	switch (uMsg)	{	case WM_INITDIALOG:	{		Metadata *meta = GetMeta(hwndDlg);		if (meta)			meta->AddRef();		else		{			meta = new Metadata();			meta->Open((wchar_t*)lParam);			SetMeta(hwndDlg,meta);		}		wchar_t genre_buf[32] = {0};		meta->id3v1.GetString("genre",genre_buf,32);		our_change=1;		SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_RESETCONTENT, 0, 0);		SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_SETCURSEL, -1, 0);		for (size_t x = 0; x != numGenres; x ++)		{			int y = (int)SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_ADDSTRING, 0, (LPARAM)id3v1_genres[x]);			SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_SETITEMDATA, y, x);			if (_wcsicmp(genre_buf,id3v1_genres[x])==0)				SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_SETCURSEL, y, 0);		}		for (int i=0; i<sizeof(strs)/sizeof(char*); i++)		{			// make sure the edit boxes are limited to id3v1 spec sizes (trickier on number type fields)			wchar_t buf[32] = {0};			SendDlgItemMessage(hwndDlg,ctrls[i],EM_SETLIMITTEXT,strs_lim[i],0);			meta->id3v1.GetString(strs[i],buf,32);			SetDlgItemTextW(hwndDlg,ctrls[i],buf);		}		if (meta->id3v1.HasData())		{			CheckDlgButton(hwndDlg,IDC_ID3V1,TRUE);			if (!config_write_id3v1)			{ // if we have id3v1 writing turned off, disable controls				for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)					EnableWindow(GetDlgItem(hwndDlg,ctrls[i]),FALSE);			}		}		else		{ // no id3v1 tag present			for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)				EnableWindow(GetDlgItem(hwndDlg,ctrls[i]),FALSE);			if (!config_create_id3v1)			{ // don't allow one to be created if the settings disallow				EnableWindow(GetDlgItem(hwndDlg,IDC_ID3V1),FALSE);			}		}		our_change=0;	}	break;	case WM_USER:		if (wParam && lParam && !our_change && !my_change_v1)		{			Metadata *meta = GetMeta(hwndDlg);			if (!meta) break;			if (!config_write_id3v1)				break;			if (!config_create_id3v1 && !meta->id3v1.HasData())				break;						wchar_t *key = (wchar_t*)wParam;			wchar_t *value = (wchar_t*)lParam;			AutoChar keyA(key);			for (int i=0; i<sizeof(strs)/sizeof(char*); i++)			{				if (_stricmp(keyA,strs[i])==0)				{					// benski> i don't think this is what we want? meta->SetExtendedData(strs[i],value);					meta->id3v1.SetString(strs[i], value);					wchar_t buf[2048]=L"";					meta->id3v1.GetString(strs[i],buf,2048);					if (!IsDlgButtonChecked(hwndDlg,IDC_ID3V1))					{						// re-enable stuff						CheckDlgButton(hwndDlg,IDC_ID3V1,TRUE);						for (int j=0; j<sizeof(ctrls)/sizeof(int); j++)						{							EnableWindow(GetDlgItem(hwndDlg,ctrls[j]),TRUE);							my_change_v1++;							SetDlgItemTextW(hwndDlg,ctrls[j],L"");							my_change_v1--;						}					}					my_change_v1++;					if (ctrls[i] == IDC_ID3_GENRE)					{						int n = (int)SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_FINDSTRINGEXACT, -1, (LPARAM)buf);						SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_SETCURSEL, n, 0);					}					else						SetDlgItemTextW(hwndDlg,ctrls[i],buf);					my_change_v1--;					break;				}			}		}		break;	case WM_COMMAND:		switch (LOWORD(wParam))		{		case IDOK:		{			// this should be done by one pane ONLY. Doesn't matter which one.			Metadata *meta = GetMeta(hwndDlg);			if (!meta) break;				Stopper stopper;			if (!_wcsicmp(lastfn, meta->filename))				stopper.Stop();			int ret = meta->Save();			stopper.Play();			wchar_t boxtitle[256] = {0};			switch(ret)			{			case SAVE_SUCCESS:				{					// cheap way to trigger a metadata reset in a thread-safe manner					wchar_t d[10] = {0};					extendedFileInfoStructW reset_info = {0};					reset_info.filename=L".mp3";					reset_info.metadata=L"artist";					reset_info.ret = d;					reset_info.retlen=10;					SendMessage(mod.hMainWindow, WM_WA_IPC, (WPARAM)&reset_info, IPC_GET_EXTENDED_FILE_INFOW);				}				break;			case SAVE_ERROR_READONLY:				MessageBox(hwndDlg, 					WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_READONLY),					WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256), 					MB_OK);				break;			case SAVE_ERROR_OPENING_FILE:				MessageBox(hwndDlg,					WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_OPENING_FILE),					WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256), 					MB_OK);					break;			case SAVE_APEV2_WRITE_ERROR:				MessageBox(hwndDlg, 					WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_APEV2),					WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256), 					MB_OK);				break;			case SAVE_LYRICS3_WRITE_ERROR:				MessageBox(hwndDlg,					WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_LYRICS3),					WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256), 					MB_OK);				break;			case SAVE_ID3V1_WRITE_ERROR:				MessageBox(hwndDlg,					WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_ID3V1),					WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256), 					MB_OK);				break;			case SAVE_ID3V2_WRITE_ERROR:				MessageBox(hwndDlg,					WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_ID3V2),					WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256), 					MB_OK);				break;			default:				MessageBox(hwndDlg,					WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_UNSPECIFIED),					WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256), 					MB_OK);				break;			}		}		break;		case IDC_ID3V1_TO_V2:		{			my_change_v1=1;			Metadata *meta = GetMeta(hwndDlg);			if (!meta) break;			for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)			{				wchar_t buf[2048]=L"";				GetDlgItemTextW(hwndDlg,ctrls[i],buf,2048);				meta->id3v2.SetString(strs[i],buf);				SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf);			}			my_change_v1=0;		}		break;		case IDC_ID3V1:		{			our_change=1;			BOOL checked = IsDlgButtonChecked(hwndDlg,IDC_ID3V1);			Metadata *meta = GetMeta(hwndDlg);			if (!meta) break;			if (!checked)				meta->id3v1.Clear();			for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)			{				EnableWindow(GetDlgItem(hwndDlg,ctrls[i]),checked);				wchar_t buf[2048]=L"";				if (checked)				{					GetDlgItemText(hwndDlg,ctrls[i],buf,2048);					if (buf[0])						meta->id3v1.SetString(strs[i],buf);				}				SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf);			}			our_change=0;		}		break;		default:			if (!our_change && !my_change_v1 && (HIWORD(wParam) == EN_CHANGE || HIWORD(wParam) == CBN_SELCHANGE))			{				our_change=1;				for (int i=0; i<sizeof(strs)/sizeof(char*); i++)				{					if (LOWORD(wParam) == ctrls[i])					{						wchar_t buf[2048]=L"";						if (HIWORD(wParam) == EN_CHANGE)							GetDlgItemTextW(hwndDlg,ctrls[i],buf,2048);						else						{							LRESULT n = SendDlgItemMessage(hwndDlg, ctrls[i], CB_GETCURSEL, 0, 0);							n = SendDlgItemMessage(hwndDlg, ctrls[i], CB_GETITEMDATA, n, 0);							if (n>=0 && n<(LRESULT)numGenres)								lstrcpyn(buf,id3v1_genres[n],2048);						}						Metadata *meta = GetMeta(hwndDlg);						if (!meta) break;						meta->id3v1.SetString(strs[i],buf);						if (!meta->id3v2.HasData())							SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf);					}				}				our_change=0;			}		}		break;	case WM_DESTROY:	{		Metadata *meta = GetMeta(hwndDlg);		if (meta) meta->Release();	}	break;	}	return 0;}static INT_PTR CALLBACK id3v2_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){	static int my_change_v2=0;	static const int ctrls[] =	{		IDC_ID3V2_TRACK,		IDC_ID3V2_TITLE,		IDC_ID3V2_ARTIST,		IDC_ID3V2_ALBUM,		IDC_ID3V2_YEAR,		IDC_ID3V2_COMMENT,		IDC_ID3V2_GENRE,		IDC_ID3V2_COMPOSER,		IDC_ID3V2_PUBLISHER,		IDC_ID3V2_MARTIST,		IDC_ID3V2_RECORD,		IDC_ID3V2_URL,		IDC_ID3V2_ENCODER,		IDC_ID3V2_ALBUM_ARTIST,		IDC_ID3V2_DISC,		IDC_TRACK_GAIN,		IDC_ALBUM_GAIN,		IDC_ID3V2_BPM,	};	static const char * strs[] =	{		"track",		"title",		"artist",		"album",		"year",		"comment",		"genre",		"composer",		"publisher",		"originalartist",		"copyright",		"url",		"tool",		"albumartist",		"disc",		"replaygain_track_gain", // 15		"replaygain_album_gain", // 16		"bpm",	};	switch (uMsg)	{	case WM_INITDIALOG:	{		our_change=1;		SendDlgItemMessage(hwndDlg, IDC_ID3V2_GENRE, CB_RESETCONTENT, 0, 0);		for (size_t x = 0; x != numGenres; x ++)		{			int y = (int)SendDlgItemMessage(hwndDlg, IDC_ID3V2_GENRE, CB_ADDSTRING, 0, (LPARAM)id3v1_genres[x]);			SendDlgItemMessage(hwndDlg, IDC_ID3V2_GENRE, CB_SETITEMDATA, y, x);		}		Metadata *meta = GetMeta(hwndDlg);		if (meta)			meta->AddRef();		else		{			meta = new Metadata();			meta->Open((wchar_t*)lParam);			SetMeta(hwndDlg,meta);		}		for (int i=0; i<sizeof(strs)/sizeof(char*); i++)		{			wchar_t buf[32768] = {0};			meta->id3v2.GetString(strs[i],buf,32768);			if((i == 15 || i == 16) && buf[0])			{				SetDlgItemTextW(hwndDlg,ctrls[i],L"buf");				// this isn't nice but it localises the RG values				// for display as they're saved in the "C" locale				double value = _wtof_l(buf,WASABI_API_LNG->Get_C_NumericLocale());				StringCchPrintfW(buf,64,L"%-+.2f dB", value);			}			SetDlgItemTextW(hwndDlg,ctrls[i],buf);		}		if (meta->id3v2.HasData())			CheckDlgButton(hwndDlg,IDC_ID3V2,TRUE);		else			for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)				EnableWindow(GetDlgItem(hwndDlg,ctrls[i]),FALSE);		our_change=0;	}	break;	case WM_USER:		if (wParam && lParam && !our_change && !my_change_v2)		{			Metadata *meta = GetMeta(hwndDlg);			if (!meta) break;			wchar_t *key = (wchar_t*)wParam;			wchar_t *value = (wchar_t*)lParam;			AutoChar keyA(key);			for (int i=0; i<sizeof(strs)/sizeof(char*); i++)			{				if (_stricmp(keyA,strs[i])==0)				{					// benski> cut? i don't think this is what we want: meta->SetExtendedData(strs[i],value);					meta->id3v2.SetString(strs[i], value);					wchar_t buf[32768]=L"";					meta->id3v2.GetString(strs[i],buf,32768);					if (!IsDlgButtonChecked(hwndDlg,IDC_ID3V2))					{						// re-enable items						CheckDlgButton(hwndDlg,IDC_ID3V2,TRUE);						for (int j=0; j<sizeof(ctrls)/sizeof(int); j++)						{							EnableWindow(GetDlgItem(hwndDlg,ctrls[j]),TRUE);							my_change_v2++;							SetDlgItemTextW(hwndDlg,ctrls[j],L"");							my_change_v2--;						}					}					my_change_v2++;					SetDlgItemTextW(hwndDlg,ctrls[i],buf);					my_change_v2--;					break;				}			}		}		break;	case WM_COMMAND:		switch (LOWORD(wParam))		{		case IDC_ID3V2_TO_V1:		{			Metadata *meta = GetMeta(hwndDlg);			if (!meta) break;			my_change_v2=1;			for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)			{				wchar_t buf[32768]=L"";				GetDlgItemTextW(hwndDlg,ctrls[i],buf,32768);				meta->id3v1.SetString(strs[i],buf);				meta->GetExtendedData(strs[i], buf, 32768);				SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf);			}			my_change_v2=0;		}		break;		case IDC_ID3V2:		{			our_change=1;			BOOL checked = IsDlgButtonChecked(hwndDlg,IDC_ID3V2);			Metadata *meta = GetMeta(hwndDlg);			if (!meta) break;			if (!checked)				meta->id3v2.Clear();			for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)			{				EnableWindow(GetDlgItem(hwndDlg,ctrls[i]),checked);				wchar_t buf[32768]=L"";				if (checked)				{					GetDlgItemText(hwndDlg,ctrls[i],buf,32768);					if (buf[0])						meta->id3v2.SetString(strs[i],buf);				}				meta->GetExtendedData(strs[i],buf,32768);				SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf);			}			our_change=0;		}		break;		case IDOK:			{						extern Metadata *m_ext_get_mp3info;		if (m_ext_get_mp3info)			m_ext_get_mp3info->Release();		m_ext_get_mp3info=0;			}			break;		default:			if (!our_change && !my_change_v2 && (HIWORD(wParam) == EN_CHANGE || HIWORD(wParam) == CBN_SELCHANGE || HIWORD(wParam) == CBN_EDITCHANGE || HIWORD(wParam) == CBN_EDITUPDATE))			{				our_change=1;				for (int i=0; i<sizeof(strs)/sizeof(char*); i++)				{					if (LOWORD(wParam) == ctrls[i])					{						wchar_t buf[32768] = {0};						if (HIWORD(wParam) == EN_CHANGE)							GetDlgItemTextW(hwndDlg,ctrls[i],buf,32768);						else						{							LRESULT n = SendMessage(GetDlgItem(hwndDlg, ctrls[i]), CB_GETCURSEL, 0, 0);							n = SendMessage(GetDlgItem(hwndDlg, ctrls[i]), CB_GETITEMDATA, n, 0);							if (n>=0 && n<(LRESULT)numGenres)								lstrcpyn(buf,id3v1_genres[n],32768);							else{								GetDlgItemTextW(hwndDlg,ctrls[i],buf,32768);							}						}						Metadata *meta = GetMeta(hwndDlg);						if (!meta) break;						meta->id3v2.SetString(strs[i],buf);						meta->GetExtendedData(strs[i],buf,32768);						SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf);					}				}				our_change=0;			}		}		break;	case WM_DESTROY:	{		Metadata *meta = GetMeta(hwndDlg);		if (meta)			meta->Release();	}	break;	}	return 0;}static INT_PTR CALLBACK lyrics3_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){	static const int ctrls[] =	{		IDC_LYRICS3_TITLE,		IDC_LYRICS3_ARTIST,		IDC_LYRICS3_ALBUM,	};	static const char * strs[] =	{		"title",		"artist",		"album",	};	switch (uMsg)	{	case WM_INITDIALOG:	{		Metadata *meta = GetMeta(hwndDlg);		if (meta)			meta->AddRef();		else		{			meta = new Metadata();			meta->Open((wchar_t*)lParam);			SetMeta(hwndDlg,meta);		}		for (int i=0; i<sizeof(strs)/sizeof(char*); i++)		{			wchar_t buf[2048] = {0};			SendDlgItemMessage(hwndDlg,ctrls[i],EM_SETLIMITTEXT,250,0);			meta->lyrics3.GetString(strs[i],buf,250);			SetDlgItemTextW(hwndDlg,ctrls[i],buf);		}		if (meta->lyrics3.HasData())			CheckDlgButton(hwndDlg,IDC_LYRICS3,TRUE);	}	break;	case WM_COMMAND:		switch (LOWORD(wParam))		{		case IDC_LYRICS3:		{			BOOL checked = IsDlgButtonChecked(hwndDlg,IDC_LYRICS3);			Metadata *meta = GetMeta(hwndDlg);			if (!meta) break;			if (!checked)				meta->lyrics3.Clear();			else	// clear the dirty state if re-enabled so we don't lose the lyrics3 tag				meta->lyrics3.ResetDirty();			for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)			{				EnableWindow(GetDlgItem(hwndDlg,ctrls[i]),checked);				wchar_t buf[2048]=L"";				if (checked)				{					GetDlgItemText(hwndDlg,ctrls[i],buf,2048);					if (buf[0])						meta->lyrics3.SetString(strs[i],buf);				}				meta->GetExtendedData(strs[i],buf,2048);				// if we don't flag this then we can lose info in the id3v1 and v2 tags which is definitely bad				our_change++;				SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf);				our_change--;			}		}		break;		}		break;	case WM_DESTROY:	{		Metadata *meta = GetMeta(hwndDlg);		if (meta) meta->Release();	}	break;	}	return 0;}/* ================APEv2 Editor Tab================ */static INT_PTR CALLBACK apev2_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){	static W_ListView listview;	static int my_change_ape=0;	switch (uMsg)	{		case WM_NOTIFYFORMAT:			return NFR_UNICODE;		case WM_INITDIALOG:		{			our_change++;			Metadata *meta = GetMeta(hwndDlg);			if (meta)			{				meta->AddRef();			}			else			{				meta = new Metadata();				meta->Open((wchar_t*)lParam);				SetMeta(hwndDlg,meta);			}			if (meta->apev2.HasData())				CheckDlgButton(hwndDlg,IDC_APEV2,TRUE);			listview.setwnd(GetDlgItem(hwndDlg, IDC_APE_LIST));			listview.SetDoubleBuffered(true);			listview.AddCol(WASABI_API_LNGSTRINGW(IDS_NAME), 82);			listview.AddCol(WASABI_API_LNGSTRINGW(IDS_VALUE), 160);			listview.SetVirtualCount((int)meta->apev2.GetNumItems());			listview.AutoSizeColumn(0);			listview.AutoSizeColumn(1);			SetDlgItemTextW(hwndDlg,IDC_APE_KEY,L"");			SetDlgItemTextW(hwndDlg,IDC_APE_VALUE,L"");			EnableWindow(GetDlgItem(hwndDlg,IDC_APE_KEY),FALSE);			EnableWindow(GetDlgItem(hwndDlg,IDC_APE_VALUE),FALSE);			EnableWindow(GetDlgItem(hwndDlg,IDC_APE_DELETE),FALSE);			our_change--;			return 1;		}		break;		case WM_COMMAND:			switch(LOWORD(wParam))			{				case IDC_APE_KEY:				case IDC_APE_VALUE:					if(HIWORD(wParam) == EN_CHANGE) 					{						int selected = listview.GetNextSelected();						if (selected != LB_ERR)						{							listview.RefreshItem(selected);						}					}					else if(HIWORD(wParam) == EN_KILLFOCUS) 					{						Metadata *meta = GetMeta(hwndDlg);						if (!meta) break;						my_change_ape++;						char key[1024] = {0};						wchar_t value[32768] = {0};						GetDlgItemTextA(hwndDlg, IDC_APE_KEY, key, 1024);						GetDlgItemText(hwndDlg, IDC_APE_VALUE, value, 32768);						int selected = listview.GetNextSelected();						if (selected != LB_ERR)						{							meta->apev2.SetKeyValueByIndex(selected, key, value);						}						const wchar_t *winamp_key = APE::MapApeKeyToWinampKeyW(key);						if (winamp_key)						{							our_change++;							SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)winamp_key,(WPARAM)value);							our_change--;						}						my_change_ape--;					}					break;			case IDC_APEV2:				{					BOOL checked = IsDlgButtonChecked(hwndDlg,IDC_APEV2);					Metadata *meta = GetMeta(hwndDlg);					if (!meta) break;					if (!checked)						meta->apev2.MarkClear();					else	// clear the dirty state if re-enabled so we don't lose the apev2 tag						meta->apev2.ResetDirty();				}				break;			case IDC_DELETE_ALL:				{					Metadata *meta = GetMeta(hwndDlg);					if (!meta) break;					my_change_ape++;					listview.UnselectAll();					meta->apev2.Clear();					listview.SetVirtualCount((int)meta->apev2.GetNumItems());					my_change_ape--;				}				break;			case IDC_APE_ADD:				{					Metadata *meta = GetMeta(hwndDlg);					if (!meta) break;					int index = listview.GetCount();					if (meta->apev2.AddItem() == APEv2::APEV2_SUCCESS)					{						listview.SetVirtualCount((int)meta->apev2.GetNumItems());						listview.SetSelected(index);						listview.ScrollTo(index);						SetFocus(GetDlgItem(hwndDlg, IDC_APE_KEY));						}				}				break;			case IDC_APE_DELETE:				{					Metadata *meta = GetMeta(hwndDlg);					if (!meta) break;					int selected = listview.GetNextSelected();					if (selected != LB_ERR)					{						listview.UnselectAll();						meta->apev2.RemoveItem(selected);						listview.SetVirtualCount((int)meta->apev2.GetNumItems());					}				}				break;			}			break;		case WM_NOTIFY:		{			LPNMHDR l=(LPNMHDR)lParam;			if (l->idFrom==IDC_APE_LIST) switch (l->code)			{				case LVN_GETDISPINFO:				{					Metadata *meta = GetMeta(hwndDlg);					if (meta)					{						NMLVDISPINFO *lpdi = (NMLVDISPINFO*) l;						if (lpdi->item.mask & LVIF_TEXT)						{							int selected = listview.GetNextSelected();							switch (lpdi->item.iSubItem)							{								case 0:								{									if (lpdi->item.iItem == selected)									{										GetDlgItemText(hwndDlg, IDC_APE_KEY, lpdi->item.pszText, lpdi->item.cchTextMax);									}									else									{										const char *key=0;										meta->apev2.EnumValue(lpdi->item.iItem, &key, 0, 0);										MultiByteToWideCharSZ(CP_ACP, 0, key?key:"", -1, lpdi->item.pszText, lpdi->item.cchTextMax);									}								}								return 0;								case 1:								{									if (lpdi->item.iItem == selected)									{										GetDlgItemText(hwndDlg, IDC_APE_VALUE,  lpdi->item.pszText, lpdi->item.cchTextMax);									}									else									{										const char *key=0;										meta->apev2.EnumValue(lpdi->item.iItem, &key, lpdi->item.pszText, lpdi->item.cchTextMax);									}								}								return 0;							}						}					}				}				break;				case LVN_KEYDOWN:				break;				case LVN_ITEMCHANGED:				{					my_change_ape++;					LPNMLISTVIEW lv=(LPNMLISTVIEW)lParam;					if (lv->uNewState & LVIS_SELECTED)					{						Metadata *meta = GetMeta(hwndDlg);						if (meta)						{							const char *key=0;							wchar_t value[32768] = {0};							meta->apev2.EnumValue(lv->iItem, &key, value, 32768);							SetDlgItemTextA(hwndDlg,IDC_APE_KEY,key);							SetDlgItemText(hwndDlg,IDC_APE_VALUE,value);							BOOL editable = meta->apev2.IsItemReadOnly(lv->iItem)?FALSE:TRUE;							EnableWindow(GetDlgItem(hwndDlg,IDC_APE_KEY),editable);							EnableWindow(GetDlgItem(hwndDlg,IDC_APE_VALUE),editable);							EnableWindow(GetDlgItem(hwndDlg,IDC_APE_DELETE),TRUE);							listview.RefreshItem(lv->iItem);						}					}					if (lv->uOldState & LVIS_SELECTED)					{						SetDlgItemTextW(hwndDlg,IDC_APE_KEY,L"");						SetDlgItemTextW(hwndDlg,IDC_APE_VALUE,L"");						EnableWindow(GetDlgItem(hwndDlg,IDC_APE_KEY),FALSE);						EnableWindow(GetDlgItem(hwndDlg,IDC_APE_VALUE),FALSE);						EnableWindow(GetDlgItem(hwndDlg,IDC_APE_DELETE),FALSE);					}					my_change_ape--;				}			}		}		break;		case WM_USER:			if (wParam && lParam && !our_change && !my_change_ape)			{				Metadata *meta = GetMeta(hwndDlg);				if (meta)				{					const wchar_t *keyW = (const wchar_t *)wParam;					const wchar_t *value = (const wchar_t *)lParam;					AutoChar key(keyW);					my_change_ape++;					meta->apev2.SetString(key, value);					listview.UnselectAll();					listview.SetVirtualCount((int)meta->apev2.GetNumItems());					listview.RefreshAll();					my_change_ape--;				}			}			break;		case WM_DESTROY:		{			Metadata *meta = GetMeta(hwndDlg);			if (meta) meta->Release();		}		break;	}	return 0;}extern "C"{	// return 1 if you want winamp to show it's own file info dialogue, 0 if you want to show your own (via In_Module.InfoBox)	// if returning 1, remember to implement winampGetExtendedFileInfo("formatinformation")!	__declspec(dllexport) int winampUseUnifiedFileInfoDlg(const wchar_t * fn)	{		if (!_wcsnicmp(fn, L"file://", 7)) fn += 7;		if (PathIsURLW(fn)) return 2;		return 1;	}	// should return a child window of 513x271 pixels (341x164 in msvc dlg units), or return NULL for no tab.	// Fill in name (a buffer of namelen characters), this is the title of the tab (defaults to "Advanced").	// filename will be valid for the life of your window. n is the tab number. This function will first be	// called with n == 0, then n == 1 and so on until you return NULL (so you can add as many tabs as you like).	// The window you return will recieve WM_COMMAND, IDOK/IDCANCEL messages when the user clicks OK or Cancel.	// when the user edits a field which is duplicated in another pane, do a SendMessage(GetParent(hwnd),WM_USER,(WPARAM)L"fieldname",(LPARAM)L"newvalue");	// this will be broadcast to all panes (including yours) as a WM_USER.	__declspec(dllexport) HWND winampAddUnifiedFileInfoPane(int n, const wchar_t * filename, HWND parent, wchar_t *name, size_t namelen)	{		if (n == 0)		{			SetPropW(parent,L"INBUILT_NOWRITEINFO", (HANDLE)1);			lstrcpyn(name,L"ID3v1", (int)namelen);			return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_ID3V1, parent, id3v1_dlgproc, (LPARAM)filename);		}		if (n == 1)		{			lstrcpyn(name,L"ID3v2", (int)namelen);			return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_ID3V2, parent, id3v2_dlgproc, (LPARAM)filename);		}		if (n == 2)		{			Metadata *meta = (Metadata *)GetPropW(parent, L"mp3info");			if (meta->lyrics3.HasData())			{				lstrcpyn(name,L"Lyrics3", (int)namelen);				return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_LYRICS3, parent, lyrics3_dlgproc, (LPARAM)filename);			}			else if (meta->apev2.HasData())			{				lstrcpyn(name,L"APEv2", (int)namelen);				return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_APEV2, parent, apev2_dlgproc, (LPARAM)filename);			}		}		if (n == 3)		{			Metadata *meta = (Metadata *)GetPropW(parent, L"mp3info");			if (meta->lyrics3.HasData() && meta->apev2.HasData())			{				lstrcpyn(name,L"APEv2", (int)namelen);				return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_APEV2, parent, apev2_dlgproc, (LPARAM)filename);			}		}		return NULL;	}}
 |