winamp/Src/Winamp/FileInfo.cpp
2024-09-24 14:54:57 +02:00

2534 lines
86 KiB
C++

// the modern file info box
#include "Main.h"
#include "resource.h"
#include "api.h"
#include "../nu/AutoWide.h"
#include <tataki/export.h>
#include <tataki/bitmap/bitmap.h>
#include <tataki/canvas/bltcanvas.h>
#include <api/service/waservicefactory.h>
#include <api/service/svcs/svc_imgload.h>
#include "language.h"
#ifndef IGNORE_API_GRACENOTE
#ifndef _WIN64
#include "../gracenote/api_gracenote.h"
#endif
#endif
#include "../Agave/Language/api_language.h"
#include "decodefile.h"
#include "../nu/AutoLock.h"
#include <api/service/svcs/svc_imgwrite.h>
#include "../Plugins/General/gen_ml/ml.h"
#include <vector>
extern Nullsoft::Utility::LockGuard getMetadataGuard;
#define TEXTBUFFER_MAX 65536
enum FileInfoMode {
FILEINFO_MODE_0,
FILEINFO_MODE_1,
};
int ModernInfoBox(In_Module * in, FileInfoMode mode, const wchar_t * filename, HWND parent);
static INT_PTR CALLBACK FileInfo(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK FileInfo_Metadata(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK FileInfo_Artwork(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
typedef HWND (__cdecl *AddUnifiedFileInfoPane)(int n, const wchar_t * filename, HWND parent, wchar_t *name, size_t namelen);
class info_params
{
public:
const wchar_t * filename;
In_Module * in;
std::vector<HWND> tabs;
FileInfoMode mode;
size_t initial_position, length;
void (*infoBoxExCallback)(size_t position, wchar_t filepath[FILENAME_SIZE]);
private:
wchar_t *buffer_; // to track allocations made by this object
public:
info_params(FileInfoMode mode, const wchar_t* filename, In_Module * in)
{
if (filename) {
// In order to prevent jtfe 1.33 from crashing winamp, we need to make sure that
// this->filename doesn't point to the begining of the allocated block, to do
// this, buffer_offset set to 2 (4 bytes).
size_t buffer_offset = 2; // "voodoo magic"
size_t filename_length = wcslen(filename);
size_t buffer_size = filename_length + 1;
buffer_ = reinterpret_cast<wchar_t*>(malloc((buffer_size + buffer_offset)*sizeof(wchar_t)));
if (buffer_) {
wchar_t *filename_buffer = buffer_ + buffer_offset;
wcsncpy_s(filename_buffer, buffer_size, filename, filename_length);
filename_buffer[filename_length] = '\0';
this->filename = filename_buffer;
} else {
// oops, looks like we out of memory (extremly rare but possible). Probably best thing,
// every responsible person should do at this point is to crash app asap,
// ...but we also can assign filename pointer directly and let nasty things happen
// somewhere later (as of writing of this comment, info_params constructed right before
// going in to the modal loop, which makes it kind of blocking call).
this->filename = filename;
}
}
else {
buffer_ = NULL;
this->filename = filename;
}
this->mode = mode;
this->in = in;
this->initial_position = 0;
this->length = 0;
}
~info_params() {
free(buffer_);
}
};
int ModernInfoBox(In_Module * in, FileInfoMode mode, const wchar_t * filename, HWND parent)
{
Tataki::Init(WASABI_API_SVC);
info_params params(mode, filename, in);
int ret = (int)LPDialogBoxParamW(IDD_FILEINFO, parent, FileInfo, (LPARAM)&params);
Tataki::Quit();
return ret;
}
static VOID WINAPI OnSelChanged(HWND hwndDlg, HWND hwndTab, info_params * p)
{
ShowWindow(p->tabs[config_last_fileinfo_page], SW_HIDE);
EnableWindow(p->tabs[config_last_fileinfo_page], 0);
config_last_fileinfo_page=TabCtrl_GetCurSel(hwndTab);
ShowWindow(p->tabs[config_last_fileinfo_page], SW_SHOWNA);
EnableWindow(p->tabs[config_last_fileinfo_page], 1);
if (GetFocus() != hwndTab)
{
SetFocus(p->tabs[config_last_fileinfo_page]);
}
}
static HWND CreateTab(FileInfoMode mode, int n, const wchar_t *file, HWND parent, wchar_t * name, size_t namelen, AddUnifiedFileInfoPane aufip)
{
switch (n)
{
case 0:
if (mode == FILEINFO_MODE_0) {
getStringW(IDS_BASICINFO, name, namelen);
return LPCreateDialogW(IDD_FILEINFO_METADATA , parent, (WNDPROC)FileInfo_Metadata);
} else {
getStringW(IDS_STREAMINFO, name, namelen);
return LPCreateDialogW(IDD_FILEINFO_STREAMDATA, parent, (WNDPROC)FileInfo_Metadata);
}
case 1:
getStringW(IDS_ARTWORK,name,namelen);
return LPCreateDialogW(IDD_FILEINFO_ARTWORK,parent,(WNDPROC)FileInfo_Artwork);
default:
if (mode == FILEINFO_MODE_0) {
getStringW(IDS_ADVANCED,name,namelen);
if (aufip) return aufip(n-2,file,parent,name,namelen);
}
return NULL;
}
}
static INT_PTR CALLBACK FileInfo(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
SetPropW(hwndDlg,L"INBUILT_NOWRITEINFO", (HANDLE)0);
info_params * p = (info_params *)lParam;
SetWindowLongPtrW(hwndDlg,GWLP_USERDATA,lParam);
HWND ok = GetDlgItem(hwndDlg, IDOK);
if (p->mode == FILEINFO_MODE_0) {
EnableWindow(ok, TRUE);
ShowWindow(ok, SW_SHOW);
} else {
EnableWindow(ok, FALSE);
ShowWindow(ok, SW_HIDE);
}
SetDlgItemTextW(hwndDlg,IDC_FN,p->filename);
// added 5.66 so plug-ins can get a hint that something may change...
SendMessageW(hMainWindow, WM_WA_IPC, (WPARAM)p->filename, IPC_FILE_TAG_MAY_UPDATEW);
AddUnifiedFileInfoPane aufip = (AddUnifiedFileInfoPane)GetProcAddress(p->in->hDllInstance, "winampAddUnifiedFileInfoPane");
HWND hwndTab = GetDlgItem(hwndDlg,IDC_TAB1);
wchar_t name[100] = {0};
TCITEMW tie = {0};
tie.mask = TCIF_TEXT;
tie.pszText = name;
HWND tab=NULL;
int n=0;
while (NULL != (tab = CreateTab(p->mode, n, p->filename, hwndDlg, name, 100, aufip)))
{
p->tabs.push_back(tab);
ShowWindow(tab,SW_HIDE);
if (!SendMessageW(hMainWindow,WM_WA_IPC,IPC_ISWINTHEMEPRESENT,IPC_USE_UXTHEME_FUNC))
SendMessageW(hMainWindow,WM_WA_IPC,(WPARAM)tab,IPC_USE_UXTHEME_FUNC);
SendMessageW(hwndTab, TCM_INSERTITEMW, n, (LPARAM)&tie);
n++;
}
RECT r;
GetWindowRect(hwndTab,&r);
TabCtrl_AdjustRect(hwndTab,FALSE,&r);
MapWindowPoints(NULL,hwndDlg,(LPPOINT)&r,2);
r.left += 3;
r.top += 2;
r.right -= 4;
for (size_t i=0; i < p->tabs.size(); i++)
SetWindowPos(p->tabs[i],HWND_TOP,r.left,r.top,r.right-r.left,r.bottom-r.top,SWP_NOACTIVATE);
if (config_last_fileinfo_page >= (int)p->tabs.size()) config_last_fileinfo_page = 0;
TabCtrl_SetCurSel(hwndTab,config_last_fileinfo_page);
ShowWindow(p->tabs[config_last_fileinfo_page], SW_SHOWNA);
// show alt+3 window and restore last position as applicable
POINT pt = {alt3_rect.left, alt3_rect.top};
if (!windowOffScreen(hwndDlg, pt) && !IsWindowVisible(hwndDlg))
SetWindowPos(hwndDlg, HWND_TOP, alt3_rect.left, alt3_rect.top, 0, 0, SWP_NOSIZE | SWP_NOSENDCHANGING);
return 1;
}
break;
case WM_NOTIFY:
{
LPNMHDR lpn = (LPNMHDR) lParam;
if (lpn && lpn->code==TCN_SELCHANGE)
{
info_params * p = (info_params *)GetWindowLongPtrW(hwndDlg,GWLP_USERDATA);
OnSelChanged(hwndDlg,GetDlgItem(hwndDlg,IDC_TAB1),p);
}
}
break;
case WM_USER:
{
info_params * p = (info_params *)GetWindowLongPtrW(hwndDlg,GWLP_USERDATA);
for (size_t i=0; i < p->tabs.size(); i++)
SendMessageW(p->tabs[i],uMsg,wParam,lParam);
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
{
Nullsoft::Utility::AutoLock metadata_lock(getMetadataGuard);
info_params * p = (info_params *)GetWindowLongPtrW(hwndDlg,GWLP_USERDATA);
for (size_t i=0; i < p->tabs.size(); i++)
{
SendMessageW(p->tabs[i],uMsg,wParam,lParam);
}
GetWindowRect(hwndDlg, &alt3_rect);
EndDialog(hwndDlg,0);
}
break;
case IDCANCEL:
{
Nullsoft::Utility::AutoLock metadata_lock(getMetadataGuard);
info_params * p = (info_params *)GetWindowLongPtrW(hwndDlg,GWLP_USERDATA);
for (size_t i=0; i < p->tabs.size(); i++)
{
SendMessageW(p->tabs[i],uMsg,wParam,lParam);
}
GetWindowRect(hwndDlg, &alt3_rect);
EndDialog(hwndDlg,1);
}
break;
}
break;
case WM_CLOSE:
return FileInfo(hwndDlg,WM_COMMAND,MAKEWPARAM(IDCANCEL,0),0);
}
return 0;
}
const wchar_t *genres[] =
{
L"Blues", L"Classic Rock", L"Country", L"Dance", L"Disco", L"Funk", L"Grunge", L"Hip-Hop",
L"Jazz", L"Metal", L"New Age", L"Oldies", L"Other", L"Pop", L"R&B", L"Rap", L"Reggae", L"Rock",
L"Techno", L"Industrial", L"Alternative", L"Ska", L"Death Metal", L"Pranks", L"Soundtrack",
L"Euro-Techno", L"Ambient", L"Trip-Hop", L"Vocal", L"Jazz+Funk", L"Fusion", L"Trance",
L"Classical", L"Instrumental", L"Acid", L"House", L"Game", L"Sound Clip", L"Gospel", L"Noise",
L"Alt Rock", L"Bass", L"Soul", L"Punk", L"Space", L"Meditative", L"Instrumental Pop",
L"Instrumental Rock", L"Ethnic", L"Gothic", L"Darkwave", L"Techno-Industrial",
L"Electronic", L"Pop-Folk", L"Eurodance", L"Dream", L"Southern Rock", L"Comedy", L"Cult",
L"Gangsta Rap", L"Top 40", L"Christian Rap", L"Pop/Funk", L"Jungle", L"Native American",
L"Cabaret", L"New Wave", L"Psychedelic", L"Rave", L"Showtunes", L"Trailer", L"Lo-Fi", L"Tribal",
L"Acid Punk", L"Acid Jazz", L"Polka", L"Retro", L"Musical", L"Rock & Roll", L"Hard Rock",
L"Folk", L"Folk-Rock", L"National Folk", L"Swing", L"Fast-Fusion", L"Bebop", L"Latin", L"Revival",
L"Celtic", L"Bluegrass", L"Avantgarde", L"Gothic Rock", L"Progressive Rock", L"Psychedelic Rock",
L"Symphonic Rock", L"Slow Rock", L"Big Band", L"Chorus", L"Easy Listening", L"Acoustic", L"Humour",
L"Speech", L"Chanson", L"Opera", L"Chamber Music", L"Sonata", L"Symphony", L"Booty Bass", L"Primus",
L"Porn Groove", L"Satire", L"Slow Jam", L"Club", L"Tango", L"Samba", L"Folklore",
L"Ballad", L"Power Ballad", L"Rhythmic Soul", L"Freestyle", L"Duet", L"Punk Rock", L"Drum Solo",
L"A Cappella", L"Euro-House", L"Dance Hall", L"Goa", L"Drum & Bass", L"Club-House",
L"Hardcore", L"Terror", L"Indie", L"BritPop", L"Afro-Punk", L"Polsk Punk", L"Beat",
L"Christian Gangsta Rap", L"Heavy Metal", L"Black Metal", L"Crossover", L"Contemporary Christian",
L"Christian Rock", L"Merengue", L"Salsa", L"Thrash Metal", L"Anime", L"JPop", L"Synthpop",
L"Abstract", L"Art Rock", L"Baroque", L"Bhangra", L"Big Beat", L"Breakbeat", L"Chillout", L"Downtempo", L"Dub", L"EBM", L"Eclectic", L"Electro",
L"Electroclash", L"Emo", L"Experimental", L"Garage", L"Global", L"IDM", L"Illbient", L"Industro-Goth", L"Jam Band", L"Krautrock", L"Leftfield", L"Lounge",
L"Math Rock", L"New Romantic", L"Nu-Breakz", L"Post-Punk", L"Post-Rock", L"Psytrance", L"Shoegaze", L"Space Rock", L"Trop Rock", L"World Music", L"Neoclassical",
L"Audiobook", L"Audio Theatre", L"Neue Deutsche Welle", L"Podcast", L"Indie Rock", L"G-Funk", L"Dubstep", L"Garage Rock", L"Psybient",
L"Glam Rock", L"Dream Pop", L"Merseybeat", L"K-Pop", L"Chiptune", L"Grime", L"Grindcore", L"Indietronic", L"Indietronica", L"Jazz Rock", L"Jazz Fusion",
L"Post-Punk Revival", L"Electronica", L"Psychill", L"Ethnotronic", L"Americana", L"Ambient Dub", L"Digital Dub", L"Chillwave", L"Stoner Rock",
L"Slowcore", L"Softcore", L"Flamenco", L"Hi-NRG", L"Ethereal", L"Drone", L"Doom Metal", L"Doom Jazz", L"Mainstream", L"Glitch", L"Balearic",
L"Modern Classical", L"Mod", L"Contemporary Classical", L"Psybreaks", L"Psystep", L"Psydub", L"Chillstep", L"Berlin School",
L"Future Jazz", L"Djent", L"Musique Concrète", L"Electroacoustic", L"Folktronica", L"Texas Country", L"Red Dirt",
L"Arabic", L"Asian", L"Bachata", L"Bollywood", L"Cajun", L"Calypso", L"Creole", L"Darkstep", L"Jewish", L"Reggaeton", L"Smooth Jazz",
L"Soca", L"Spiritual", L"Turntablism", L"Zouk", L"Neofolk", L"Nu Jazz", L"Psychobilly", L"Rockabilly", L"Schlager & Volksmusik",
};
const size_t numberOfGenres = sizeof(genres) / sizeof(genres[0]);
const wchar_t * strfields[] =
{
L"artist",
L"album",
L"albumartist",
L"title",
L"year",
L"genre",
L"comment",
L"composer",
L"publisher",
L"disc",
L"track",
L"bpm",
L"GracenoteFileID",
L"GracenoteExtData"
};
const int strfieldctrls[] =
{
IDC_ARTIST,
IDC_ALBUM,
IDC_ALBUM_ARTIST,
IDC_TITLE,
IDC_YEAR,
IDC_GENRE,
IDC_COMMENT,
IDC_COMPOSER,
IDC_PUBLISHER,
IDC_DISC,
IDC_TRACK,
IDC_BPM,
IDC_GN_FILEID,
IDC_GN_EXTDATA
};
const int staticstrfieldctrls[] =
{
IDC_STATIC_ARTIST,
IDC_STATIC_ALBUM,
IDC_STATIC_ALBUM_ARTIST,
IDC_STATIC_TITLE,
IDC_STATIC_YEAR,
IDC_STATIC_GENRE,
IDC_STATIC_COMMENT,
IDC_STATIC_COMPOSER,
IDC_STATIC_PUBLISHER,
IDC_STATIC_DISC,
IDC_STATIC_TRACK,
IDC_STATIC_BPM,
0,
0
};
const wchar_t * streamStrfields[] =
{
L"streamtitle",
L"streamgenre",
L"streamurl",
L"streamname",
L"streamcurrenturl",
L"streammetadata",
};
const int streamStrfieldctrls[] =
{
IDC_TITLE,
IDC_GENRE,
IDC_URL,
IDC_STATION,
IDC_PLAY_URL,
IDC_EXTRA_METADATA,
};
const int staticStreamStrfieldctrls[] =
{
IDC_STATIC_TITLE,
IDC_STATIC_GENRE,
IDC_STATIC_URL,
IDC_STATIC_STATION,
IDC_STATIC_PLAY_URL,
0,
};
#ifndef IGNORE_API_GRACENOTE
#ifndef _WIN64
static IConnectionPoint *GetConnectionPoint(IUnknown *punk, REFIID riid)
{
if (!punk)
return 0;
IConnectionPointContainer *pcpc;
IConnectionPoint *pcp = 0;
HRESULT hr = punk->QueryInterface(IID_IConnectionPointContainer, (void **) & pcpc);
if (SUCCEEDED(hr))
{
pcpc->FindConnectionPoint(riid, &pcp);
pcpc->Release();
}
punk->Release();
return pcp;
}
class autoTagListen : public _ICDDBMusicIDManagerEvents
{
public:
autoTagListen(api_gracenote* gn, ICDDBMusicIDManager3 *musicid, wchar_t *_gracenoteFileId) : hwndDlg(0), h(0), gn(gn),musicid(musicid), done(0), abort(0), gracenoteFileId(_gracenoteFileId)
{
}
HRESULT OnTrackIDStatusUpdate(CddbMusicIDStatus Status, BSTR filename, long* _Abort)
{
if (abort) *_Abort=1;
/*switch(Status)
{
case STATUS_MUSICID_Error:
case STATUS_MUSICID_ProcessingFile:
case STATUS_MUSICID_LookingUpWaveForm:
case STATUS_MUSICID_LookingUpText:
case STATUS_MUSICID_CheckForAbort:
case STATUS_ALBUMID_Querying:
case STATUS_ALBUMID_Queried:
case STATUS_ALBUMID_Processing:
case STATUS_ALBUMID_Processed:
case STATUS_MUSICID_AnalyzingWaveForm:
case STATUS_ALBUMID_QueryingWF:
}*/
// do shit
return S_OK;
}
HRESULT OnTrackIDComplete(LONG match_code, ICddbFileInfo* pInfoIn, ICddbFileInfoList* pListOut)
{
done = 1;
if (match_code <= 1)
{
if (!abort)
{
wchar_t title[16] = {0};
MessageBoxW(h, getStringW(IDS_NO_MATCH_FOUND, NULL, 0),
getStringW(IDS_FAILED, title, 16), MB_ICONWARNING);
}
EndDialog(hwndDlg,0);
return S_OK;
}
long num = 0;
pListOut->get_Count(&num);
if (!num) return S_OK;
ICddbFileInfoPtr infotag = NULL;
pListOut->GetFileInfo(1,&infotag);
if (infotag)
{
wchar_t buf[2048] = {0};
BSTR bstr = buf;
ICddbFileTagPtr tag = NULL;
ICddbDisc2Ptr disc = NULL;
ICddbDisc2_5Ptr disc2_5 = NULL;
ICddbFileTag2_5Ptr tag2_5 = NULL;
infotag->get_Tag(&tag);
infotag->get_Disc(&disc);
if (tag)
{
tag->QueryInterface(&tag2_5);
disc->QueryInterface(&disc2_5);
#define PUTINFO(id, ctrl) if (tag) { tag->get_ ## id ## (&bstr); SetDlgItemTextW(h, ctrl, bstr); }
#define PUTINFO2(id, ctrl) if (tag2_5) { tag2_5->get_ ## id ## (&bstr); SetDlgItemTextW(h, ctrl, bstr); }
PUTINFO(LeadArtist, IDC_ARTIST);
PUTINFO(Album, IDC_ALBUM);
PUTINFO(Title, IDC_TITLE);
PUTINFO(Album, IDC_ALBUM);
if (disc2_5 == NULL
|| (FAILED(disc2_5->get_V2GenreStringPrimaryByLevel(3, &bstr))
&& FAILED(disc2_5->get_V2GenreStringPrimaryByLevel(2, &bstr))
&& FAILED(disc2_5->get_V2GenreStringPrimaryByLevel(1, &bstr))
&& FAILED(disc2_5->get_V2GenreStringPrimary(&bstr)))
)
{
PUTINFO(Genre, IDC_GENRE);
}
else
{
SetDlgItemTextW(h,IDC_GENRE,bstr);
}
}
// sending this will ensure that the genre is applied other than in the metadata page
SendMessageW(GetParent(h),WM_USER,(WPARAM)strfields[5],(LPARAM)bstr);
PUTINFO(Year, IDC_YEAR);
PUTINFO(Label, IDC_PUBLISHER);
PUTINFO(BeatsPerMinute, IDC_BPM);
PUTINFO(TrackPosition, IDC_TRACK);
PUTINFO(PartOfSet, IDC_DISC);
PUTINFO2(Composer, IDC_COMPOSER);
PUTINFO2(DiscArtist, IDC_ALBUM_ARTIST);
PUTINFO(FileId, IDC_GN_FILEID);
PUTINFO2(ExtDataSerialized, IDC_GN_EXTDATA);
#undef PUTINFO
#undef PUTINFO2
// CRASH POINT
// This is the starting cause of crashing in the cddb stuff
// Without this or with a manual AddRef() before and it'll work ok
//infotag->Release();
}
EndDialog(hwndDlg,0);
return S_OK;
}
HRESULT FillTag(ICddbFileInfo *info, BSTR filename)
{
if (info)
{
#define PUTINFO(id, ctrl) GetDlgItemTextW(h, ctrl, buf, 2048); if(buf[0]) if (infotag) { infotag->put_ ## id ## (buf); }
#define PUTINFO2(id, ctrl) GetDlgItemTextW(h, ctrl, buf, 2048); if(buf[0]) if (tag2_5) { tag2_5->put_ ## id ## (buf); }
ICddbID3TagPtr infotag = NULL;
infotag.CreateInstance(CLSID_CddbID3Tag);
ICddbFileTag2_5Ptr tag2_5 = NULL;
infotag->QueryInterface(&tag2_5);
wchar_t buf[2048] = {0};
PUTINFO(LeadArtist, IDC_ARTIST);
PUTINFO(Album, IDC_ALBUM);
PUTINFO(Title, IDC_TITLE);
PUTINFO(Album, IDC_ALBUM);
PUTINFO(Genre, IDC_GENRE);
PUTINFO(Year, IDC_YEAR);
PUTINFO(Label, IDC_PUBLISHER);
PUTINFO(BeatsPerMinute, IDC_BPM);
PUTINFO(TrackPosition, IDC_TRACK);
PUTINFO(PartOfSet, IDC_DISC);
PUTINFO2(Composer, IDC_COMPOSER);
PUTINFO2(DiscArtist, IDC_ALBUM_ARTIST);
in_get_extended_fileinfoW(filename,L"length",buf,2048);
tag2_5->put_LengthMS(buf);
if (gracenoteFileId && *gracenoteFileId)
infotag->put_FileId(gracenoteFileId);
#undef PUTINFO
#undef PUTINFO2
info->put_Tag(infotag);
}
return S_OK;
}
STDMETHODIMP STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObject)
{
if (!ppvObject)
return E_POINTER;
else if (IsEqualIID(riid, __uuidof(_ICDDBMusicIDManagerEvents)))
*ppvObject = (_ICDDBMusicIDManagerEvents *)this;
else if (IsEqualIID(riid, IID_IDispatch))
*ppvObject = (IDispatch *)this;
else if (IsEqualIID(riid, IID_IUnknown))
*ppvObject = this;
else
{
*ppvObject = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
ULONG STDMETHODCALLTYPE AddRef(void)
{
return 1;
}
ULONG STDMETHODCALLTYPE Release(void)
{
return 0;
}
HRESULT STDMETHODCALLTYPE Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
{
switch (dispid)
{
case 1: // OnTrackIDStatusUpdate, params: CddbMusicIDStatus Status, BSTR filename, long* Abort
{
long *abort = pdispparams->rgvarg[0].plVal;
BSTR filename = pdispparams->rgvarg[1].bstrVal;
CddbMusicIDStatus status = (CddbMusicIDStatus)pdispparams->rgvarg[2].lVal;
return OnTrackIDStatusUpdate(status,filename,abort);
}
case 3: // OnTrackIDComplete, params: LONG match_code, ICddbFileInfo* pInfoIn, ICddbFileInfoList* pListOut
{
IDispatch *disp1 =pdispparams->rgvarg[0].pdispVal;
IDispatch *disp2 =pdispparams->rgvarg[1].pdispVal;
long match_code = pdispparams->rgvarg[2].lVal;
ICddbFileInfoPtr pInfoIn;
ICddbFileInfoListPtr matchList;
disp1->QueryInterface(&matchList);
disp2->QueryInterface(&pInfoIn);
return OnTrackIDComplete(match_code,pInfoIn,matchList);
}
case 10: // OnGetFingerprintInfo
{
long *abort = pdispparams->rgvarg[0].plVal;
IDispatch *disp = pdispparams->rgvarg[1].pdispVal;
BSTR filename = pdispparams->rgvarg[2].bstrVal;
ICddbFileInfo *info;
disp->QueryInterface(&info);
extern DecodeFile *decodeFile;
return gn->CreateFingerprint(musicid, decodeFile, info, filename, abort);
}
break;
case 11: // OnGetTagInfo
{
//long *Abort = pdispparams->rgvarg[0].plVal;
IDispatch *disp = pdispparams->rgvarg[1].pdispVal;
BSTR filename = pdispparams->rgvarg[2].bstrVal;
ICddbFileInfo *info;
disp->QueryInterface(&info);
return FillTag(info, filename);
}
break;
}
return DISP_E_MEMBERNOTFOUND;
}
HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
{
*rgdispid = DISPID_UNKNOWN; return DISP_E_UNKNOWNNAME;
}
HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE GetTypeInfoCount(unsigned int FAR * pctinfo)
{
return E_NOTIMPL;
}
HWND hwndDlg,h;
api_gracenote* gn;
ICDDBMusicIDManager3 *musicid;
int done;
long abort;
wchar_t *gracenoteFileId;
};
static INT_PTR CALLBACK FileInfo_Autotagging(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
// have the close button disabled otherwise it might cause confusion
EnableMenuItem(GetSystemMenu(hwndDlg, 0), SC_CLOSE, MF_BYCOMMAND|MF_GRAYED);
SetWindowLong(hwndDlg,GWLP_USERDATA,lParam);
autoTagListen * p = (autoTagListen *)lParam;
p->hwndDlg = hwndDlg;
if (p->done) EndDialog(hwndDlg,0);
}
break;
}
return 0;
}
#endif
#endif
LPCWSTR RepairMutlilineString(LPWSTR pszBuffer, INT cchBufferMax)
{
if (NULL == pszBuffer || cchBufferMax < 1)
return NULL;
LPWSTR temp = (WCHAR*)calloc(cchBufferMax, sizeof(WCHAR));
if (NULL == temp) return NULL;
LPWSTR p1, p2;
p1 = pszBuffer;
p2 = temp;
INT cchLen;
for (cchLen = 0; L'\0' != *p1 && ((p2 - temp) < cchBufferMax); cchLen++)
{
if(*p1 == L'\n')
{
*p2++ = L'\r';
cchLen++;
}
*p2++ = *p1++;
}
CopyMemory(pszBuffer, temp, sizeof(WCHAR) * cchLen);
pszBuffer[cchLen] = L'\0';
free(temp);
return pszBuffer;
}
static INT_PTR CALLBACK FileInfo_Metadata(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static int my_change=0;
switch (uMsg)
{
case WM_INITDIALOG:
{
my_change=1;
int y;
SendMessageW(GetDlgItem(hwndDlg, IDC_GENRE), CB_RESETCONTENT, (WPARAM)0, 0);
SendMessageW(GetDlgItem(hwndDlg, IDC_GENRE), CB_SETCURSEL, (WPARAM)-1, 0);
for (size_t x = 0; x != numberOfGenres; x ++)
{
y = SendMessageW(GetDlgItem(hwndDlg, IDC_GENRE), CB_ADDSTRING, 0, (LPARAM)genres[x]);
SendMessageW(GetDlgItem(hwndDlg, IDC_GENRE), CB_SETITEMDATA, y, x);
}
y = SendMessageW(GetDlgItem(hwndDlg, IDC_GENRE), CB_ADDSTRING, 0, (LPARAM)L"");
SendMessageW(GetDlgItem(hwndDlg, IDC_GENRE), CB_SETITEMDATA, y, 255);
info_params * p = (info_params *)GetWindowLongPtrW(GetParent(hwndDlg),GWLP_USERDATA);
LPWSTR pszBuffer = (LPWSTR)calloc(TEXTBUFFER_MAX, sizeof(WCHAR));
if (p->mode == FILEINFO_MODE_0) {
for (int i=0; i<sizeof(strfields)/sizeof(wchar_t*); i++)
{
memset(pszBuffer, 0, sizeof(WCHAR) * TEXTBUFFER_MAX);
int result = in_get_extended_fileinfoW(p->filename,strfields[i],pszBuffer,TEXTBUFFER_MAX);
SetDlgItemTextW(hwndDlg, strfieldctrls[i], pszBuffer);
EnableWindow(GetDlgItem(hwndDlg, strfieldctrls[i]), result?TRUE:FALSE);
EnableWindow(GetDlgItem(hwndDlg, staticstrfieldctrls[i]), result?TRUE:FALSE);
}
} else {
for (int i=0; i<sizeof(streamStrfields)/sizeof(wchar_t*); i++)
{
memset(pszBuffer, 0, sizeof(WCHAR) * TEXTBUFFER_MAX);
int result = in_get_extended_fileinfoW(p->filename,streamStrfields[i],pszBuffer,TEXTBUFFER_MAX);
if (streamStrfieldctrls[i] != IDC_EXTRA_METADATA) {
SetDlgItemTextW(hwndDlg, streamStrfieldctrls[i], pszBuffer);
} else {
CheckDlgButton(hwndDlg, streamStrfieldctrls[i], result && pszBuffer[0] == L'1' ? TRUE : FALSE);
}
EnableWindow(GetDlgItem(hwndDlg, streamStrfieldctrls[i]), result ? TRUE : FALSE);
EnableWindow(GetDlgItem(hwndDlg, staticStreamStrfieldctrls[i]), result ? TRUE : FALSE);
}
}
pszBuffer[0]=0;
if (p->mode == FILEINFO_MODE_0) {
in_get_extended_fileinfoW(p->filename,L"formatinformation",pszBuffer, TEXTBUFFER_MAX);
} else {
in_get_extended_fileinfoW(p->filename,L"streaminformation",pszBuffer, TEXTBUFFER_MAX);
}
// due to quirks with the more common resource editors, is easier to just store the string
// internally only with \n and post-process to be \r\n (as here) so it will appear correctly
// on new lines as is wanted (silly multiline edit controls)
SetDlgItemTextW(hwndDlg, IDC_FORMATINFO, RepairMutlilineString(pszBuffer, TEXTBUFFER_MAX));
if (p->mode == FILEINFO_MODE_0) {
wchar_t tg[64]=L"", ag[64]=L"";
in_get_extended_fileinfoW(p->filename,L"replaygain_track_gain",tg,64);
in_get_extended_fileinfoW(p->filename,L"replaygain_album_gain",ag,64);
if (!tg[0]) getStringW(IDS_NOTPRESENT,tg,64);
else
{
// this isn't nice but it localises the RG values
// for display as they're saved in the "C" locale
double value = _wtof_l(tg,langManager->Get_C_NumericLocale());
StringCchPrintfW(tg,64,L"%-+.2f dB", value);
}
if (!ag[0]) getStringW(IDS_NOTPRESENT,ag,64);
else
{
// this isn't nice but it localises the RG values
// for display as they're saved in the "C" locale
double value = _wtof_l(ag,langManager->Get_C_NumericLocale());
StringCchPrintfW(ag,64,L"%-+.2f dB", value);
}
wchar_t tgagstr[128] = {0};
StringCbPrintfW(pszBuffer, TEXTBUFFER_MAX, getStringW(IDS_TRACK_GAIN_AND_ALBUM_GAIN,tgagstr,128),tg,ag);
SetDlgItemTextW(hwndDlg, IDC_REPLAYGAIN, RepairMutlilineString(pszBuffer, TEXTBUFFER_MAX));
my_change=0;
free(pszBuffer);
} else {
SetTimer(hwndDlg, 1, 1000, 0);
}
// test for the musicid feature not being available and remove
// the autotag button as required (useful for lite installs)
#ifndef IGNORE_API_GRACENOTE
waServiceFactory *factory = WASABI_API_SVC?WASABI_API_SVC->service_getServiceByGuid(gracenoteApiGUID):0;
api_gracenote* gn = NULL;
int remove = FALSE;
if(factory){
gn = (api_gracenote *)factory->getInterface();
if(gn){
ICDDBMusicIDManager3 *musicid = gn->GetMusicID();
if(musicid){
musicid->Shutdown();
musicid->Release();
}
else{
remove = TRUE;
}
factory->releaseInterface(gn);
}
else{
remove = TRUE;
}
}
#else
int remove = TRUE;
#endif
// remove or disable the button based on what has been
// installed or the internet access levels configured
if (remove)
{
DestroyWindow(GetDlgItem(hwndDlg,IDC_AUTOTAG));
}
else
{
if (!isInetAvailable())
{
EnableWindow(GetDlgItem(hwndDlg,IDC_AUTOTAG), FALSE);
}
}
}
break;
case WM_TIMER:
if (wParam == 1) {
info_params * p = (info_params *)GetWindowLongPtrW(GetParent(hwndDlg),GWLP_USERDATA);
if (p && p->mode == FILEINFO_MODE_1) {
LPWSTR pszBuffer = (LPWSTR)calloc(TEXTBUFFER_MAX, sizeof(WCHAR));
if (pszBuffer) {
int start = -1, end = 0;
SendDlgItemMessage(hwndDlg, IDC_FORMATINFO, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
in_get_extended_fileinfoW(p->filename, L"streaminformation", pszBuffer, TEXTBUFFER_MAX);
// due to quirks with the more common resource editors, is easier to just store the string
// internally only with \n and post-process to be \r\n (as here) so it will appear correctly
// on new lines as is wanted (silly multiline edit controls)
SetDlgItemTextW(hwndDlg, IDC_FORMATINFO, RepairMutlilineString(pszBuffer, TEXTBUFFER_MAX));
SendDlgItemMessage(hwndDlg, IDC_FORMATINFO, EM_SETSEL, start, end);
for (int i=0; i<sizeof(streamStrfields)/sizeof(wchar_t*); i++) {
memset(pszBuffer, 0, sizeof(WCHAR) * TEXTBUFFER_MAX);
int result = in_get_extended_fileinfoW(p->filename,streamStrfields[i],pszBuffer,TEXTBUFFER_MAX);
if (streamStrfieldctrls[i] != IDC_EXTRA_METADATA) {
SendDlgItemMessage(hwndDlg, streamStrfieldctrls[i], EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
SetDlgItemTextW(hwndDlg, streamStrfieldctrls[i], pszBuffer);
SendDlgItemMessage(hwndDlg, streamStrfieldctrls[i], EM_SETSEL, start, end);
} else {
CheckDlgButton(hwndDlg, streamStrfieldctrls[i], result && pszBuffer[0] == L'1' ? TRUE : FALSE);
}
EnableWindow(GetDlgItem(hwndDlg, streamStrfieldctrls[i]), result ? TRUE : FALSE);
EnableWindow(GetDlgItem(hwndDlg, staticStreamStrfieldctrls[i]), result ? TRUE : FALSE);
}
}
}
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
#ifndef IGNORE_API_GRACENOTE
#ifndef _WIN64
case IDC_AUTOTAG:
{
ICDDBMusicIDManager3 *musicid = NULL;
waServiceFactory *factory = WASABI_API_SVC?WASABI_API_SVC->service_getServiceByGuid(gracenoteApiGUID):0;
api_gracenote* gn = NULL;
if (factory)
{
gn = (api_gracenote *)factory->getInterface();
if (gn)
{
musicid = gn->GetMusicID();
}
}
if (!musicid)
{
wchar_t title[32] = {0};
if (gn)
{
if (factory)
{
factory->releaseInterface(gn);
}
gn = NULL;
}
MessageBoxW(hwndDlg, getStringW(IDS_GRACENOTE_TOOLS_NOT_INSTALLED, NULL, 0),
getStringW(IDS_ERROR, title, 32),0);
break;
}
// we have the musicid pointer, so lets try and tag this mother.
info_params * p = (info_params *)GetWindowLongPtrW(GetParent(hwndDlg),GWLP_USERDATA);
if (NULL == p || NULL == p->filename || L'\0' == p->filename)
break;
// first, see if there's a pre-existing gracenote file id
wchar_t fileid[1024]=L"";
extendedFileInfoStructW gracenote_info;
gracenote_info.filename = p->filename;
gracenote_info.metadata = L"GracenoteFileID";
gracenote_info.ret = fileid;
gracenote_info.retlen = 1024;
if (0 == SendMessageW(hMainWindow, WM_WA_IPC, (WPARAM)&gracenote_info, IPC_GET_EXTENDED_FILE_INFOW_HOOKABLE))
fileid[0] = L'\0';
ICddbFileInfoPtr info = 0;
info.CreateInstance(CLSID_CddbFileInfo);
info->put_Filename((wchar_t*)p->filename);
ICddbFileInfoList *dummy=0;
long match_code=666;
IConnectionPoint *icp = GetConnectionPoint(musicid, DIID__ICDDBMusicIDManagerEvents);
DWORD m_dwCookie = 0;
autoTagListen listen(gn,musicid, fileid);
listen.h = hwndDlg;
if (icp) icp->Advise(static_cast<IDispatch *>(&listen), &m_dwCookie);
musicid->TrackID(info, MUSICID_LOOKUP_ASYNC|MUSICID_RETURN_SINGLE|MUSICID_GET_TAG_FROM_APP|MUSICID_GET_FP_FROM_APP|MUSICID_PREFER_WF_MATCHES, &match_code, &dummy);
if (dummy)
dummy->Release();
LPDialogBoxParamW(IDD_AUTOTAGGING,hwndDlg,FileInfo_Autotagging,(LPARAM)&listen);
musicid->Shutdown();
musicid->Release();
factory->releaseInterface(gn);
}
break;
#endif
#endif
case IDOK:
if (!GetPropW(GetParent(hwndDlg),L"INBUILT_NOWRITEINFO"))
{
info_params * p = (info_params *)GetWindowLongPtrW(GetParent(hwndDlg),GWLP_USERDATA);
LPWSTR pszBuffer = (LPWSTR)calloc(TEXTBUFFER_MAX, sizeof(WCHAR));
if (pszBuffer)
{
for (int i=0; i<sizeof(strfields)/sizeof(wchar_t*); i++)
{
if (!GetDlgItemTextW(hwndDlg,strfieldctrls[i],pszBuffer, TEXTBUFFER_MAX))
pszBuffer[0] = L'\0';
in_set_extended_fileinfoW(p->filename,strfields[i], pszBuffer);
}
free(pszBuffer);
}
if (in_write_extended_fileinfo() == 0)
{
wchar_t title[256] = {0};
MessageBoxW(hwndDlg,
getStringW(IDS_METADATA_ERROR, NULL, 0),
getStringW(IDS_METADATA_ERROR_TITLE, title, 256),
MB_OK | MB_ICONWARNING);
}
}
break;
default:
if (!my_change && (HIWORD(wParam) == EN_CHANGE || HIWORD(wParam) == CBN_SELCHANGE || HIWORD(wParam) == CBN_EDITCHANGE || HIWORD(wParam) == CBN_EDITUPDATE))
{
my_change=1;
for (int i=0; i<sizeof(strfields)/sizeof(wchar_t*); i++)
{
if (LOWORD(wParam) == strfieldctrls[i])
{
if (HIWORD(wParam) != CBN_SELCHANGE)
{
LPWSTR pszBuffer = (LPWSTR)calloc(TEXTBUFFER_MAX, sizeof(WCHAR));
if (pszBuffer)
{
GetDlgItemTextW(hwndDlg,strfieldctrls[i], pszBuffer, TEXTBUFFER_MAX);
SendMessageW(GetParent(hwndDlg),WM_USER,(WPARAM)strfields[i],(LPARAM)pszBuffer);
free(pszBuffer);
}
}
else
{
int n = SendMessageW(GetDlgItem(hwndDlg, strfieldctrls[i]), CB_GETCURSEL, 0, 0);
int m = SendMessageW(GetDlgItem(hwndDlg, strfieldctrls[i]), CB_GETITEMDATA, n, 0);
if (m>=0 && m<numberOfGenres)
{
SendMessageW(GetParent(hwndDlg),WM_USER,(WPARAM)strfields[i],(LPARAM)genres[m]);
}
// handles case where genre is cleared
else if (!n && m == 255)
{
SendMessageW(GetParent(hwndDlg),WM_USER,(WPARAM)strfields[i],(LPARAM)L"");
}
else if(n == CB_ERR)
{
// if we got here then it is likely to be from a genre not in the built in list
LPWSTR pszBuffer = (LPWSTR)calloc(TEXTBUFFER_MAX, sizeof(WCHAR));
if (pszBuffer)
{
if(GetDlgItemTextW(hwndDlg,strfieldctrls[i], pszBuffer, TEXTBUFFER_MAX)){
SendMessageW(GetParent(hwndDlg),WM_USER,(WPARAM)strfields[i],(LPARAM)pszBuffer);
}
free(pszBuffer);
}
}
}
}
}
my_change=0;
}
}
break;
case WM_USER:
if (wParam && lParam && !my_change)
{
for (int i=0; i<sizeof(strfields)/sizeof(wchar_t*); i++)
if (_wcsicmp((wchar_t*)wParam,strfields[i])==0)
SetDlgItemTextW(hwndDlg,strfieldctrls[i],(wchar_t*)lParam);
}
break;
}
return 0;
}
static void Adjust(int bmpw, int bmph, int &x, int &y, int &w, int &h)
{
// maintain 'square' stretching
double aspX = (double)(w)/(double)bmpw;
double aspY = (double)(h)/(double)bmph;
double asp = min(aspX, aspY);
int newW = (int)(bmpw*asp);
int newH = (int)(bmph*asp);
x = (w - newW)/2;
y = (h - newH)/2;
w = newW;
h = newH;
}
static HBITMAP getBitmap(ARGB32 * data, int w, int h, HWND parent)
{
BITMAPINFO info={0};
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = w;
info.bmiHeader.biHeight = -h;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = 32;
info.bmiHeader.biCompression = BI_RGB;
HDC dc = GetDC(parent);
HBITMAP bm = CreateCompatibleBitmap(dc,w,h);
SetDIBits(dc,bm,0,h,data,&info,DIB_RGB_COLORS);
ReleaseDC(parent,dc);
return bm;
}
// these two are also used by AlbumArtRetrival.cpp
ARGB32 * decompressImage(const void *data, int datalen, int * dataW, int * dataH)
{
ARGB32* ret=NULL;
FOURCC imgload = svc_imageLoader::getServiceType();
int n = (int) WASABI_API_SVC->service_getNumServices(imgload);
for (int i=0; i<n; i++)
{
waServiceFactory *sf = WASABI_API_SVC->service_enumService(imgload,i);
if (sf)
{
svc_imageLoader * l = (svc_imageLoader*)sf->getInterface();
if (l)
{
if (l->testData(data,datalen))
{
ret = l->loadImage(data,datalen,dataW,dataH);
sf->releaseInterface(l);
break;
}
sf->releaseInterface(l);
}
}
}
return ret;
}
HBITMAP getBitmap(ARGB32 * data, int dw, int dh, int targetW, int targetH, HWND parent)
{
HQSkinBitmap bm(data,dw,dh);
int x=0,y=0,w=targetW,h=targetH;
Adjust(dw,dh,x,y,w,h);
BltCanvas canv(targetW,targetH);
bm.stretch(&canv,x,y,w,h);
return getBitmap((ARGB32*)canv.getBits(),targetW,targetH,parent);
}
void EnableArtFrame(HWND hwndDlg, BOOL enable)
{
const int artFrameElements[] =
{
IDC_STATIC_FRAME,
IDC_ARTHOLDER,
IDC_BUTTON_CHANGE,
IDC_BUTTON_SAVEAS,
IDC_BUTTON_COPY,
IDC_BUTTON_PASTE,
IDC_BUTTON_DELETE,
};
for (int i=0; i<sizeof(artFrameElements)/sizeof(int); i++)
EnableWindow(GetDlgItem(hwndDlg,artFrameElements[i]),enable);
SetDlgItemTextW(hwndDlg,IDC_STATIC_FRAME,getStringW(IDS_NO_IMAGE,0,0));
}
class EditArtItem
{
public:
ARGB32 * bits;
int w;
int h;
void *data;
size_t datalen;
wchar_t *mimetype;
bool dirty;
EditArtItem(ARGB32 *bits, int w, int h, bool dirty=false) : bits(bits), w(w), h(h), data(0), datalen(0), mimetype(0), dirty(dirty) {}
~EditArtItem()
{
if (bits) WASABI_API_MEMMGR->sysFree(bits);
if (mimetype) free(mimetype);
if (data) WASABI_API_MEMMGR->sysFree(data);
}
};
static EditArtItem * getCurrentArtItem(HWND hwndDlg)
{
int sel = SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_GETCURSEL,0,0);
if (sel == -1)
return NULL;
EditArtItem *e = (EditArtItem *)SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_GETITEMDATA,sel,0);
if (e != (EditArtItem *)-1)
return e;
return NULL;
}
static void GetSize(HBITMAP bm, int &w, int &h, HWND hwndDlg)
{
HDC dc = GetDC(hwndDlg);
BITMAPINFO info={0};
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
GetDIBits(dc,bm,0,0,NULL,&info,DIB_RGB_COLORS);
w = abs(info.bmiHeader.biWidth);
h = abs(info.bmiHeader.biHeight);
ReleaseDC(hwndDlg,dc);
}
static bool AddNewArtItem(HWND hwndDlg)
{
wchar_t buf[100]=L"";
GetDlgItemTextW(hwndDlg,IDC_COMBO_ARTTYPE,buf,100);
if (!buf[0]) return false;
int s=SendDlgItemMessageW(hwndDlg,IDC_ARTLIST,LB_FINDSTRINGEXACT, (WPARAM)-1,(LPARAM)buf);
if (s != LB_ERR && SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_GETCOUNT,0,0))
{
// user has selected something already in our list
SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_SETCURSEL,s,0);
SendMessageW(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_ARTLIST,LBN_SELCHANGE),0);
return true;
}
// let's add this new item
s=SendDlgItemMessageW(hwndDlg,IDC_ARTLIST,LB_ADDSTRING,0,(LPARAM)buf);
if (s == LB_ERR) return false;
EditArtItem *e = new EditArtItem(0,0,0);
SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_SETITEMDATA,s,(LPARAM)e);
SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_SETCURSEL,s,0);
EnableArtFrame(hwndDlg,TRUE);
return true;
}
static svc_imageLoader *FindImageLoader(const wchar_t *filespec, waServiceFactory **factory)
{
FOURCC imgload = svc_imageLoader::getServiceType();
int n = (int) WASABI_API_SVC->service_getNumServices(imgload);
for (int i=0; i<n; i++)
{
waServiceFactory *sf = WASABI_API_SVC->service_enumService(imgload,i);
if (sf)
{
svc_imageLoader * l = (svc_imageLoader*)sf->getInterface();
if (l)
{
if (l->isMine(filespec))
{
*factory = sf;
return l;
}
sf->releaseInterface(l);
}
}
}
return NULL;
}
static ARGB32 *loadImgFromFile(const wchar_t *file, int *len, int *w, int *h, wchar_t ** mime, ARGB32** imageData)
{
waServiceFactory *sf;
svc_imageLoader *loader = FindImageLoader(file, &sf);
if (loader)
{
HANDLE hf = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (hf != INVALID_HANDLE_VALUE)
{
*len = GetFileSize(hf, 0);
HANDLE hmap = CreateFileMapping(hf, 0, PAGE_READONLY, 0, 0, 0);
if (hmap)
{
void *data = MapViewOfFile(hmap, FILE_MAP_READ, 0, 0, 0);
if (data)
{
if (loader->testData(data,*len))
{
ARGB32* im = loader->loadImage(data,*len,w,h);
if (im && !_wcsicmp(loader->mimeType(), L"image/jpeg"))
{
*mime = _wcsdup(L"jpg");
*imageData = (ARGB32*)WASABI_API_MEMMGR->sysMalloc(*len);
memcpy(*imageData, data, *len);
}
UnmapViewOfFile(data);
CloseHandle(hmap);
CloseHandle(hf);
sf->releaseInterface(loader);
return im;
}
UnmapViewOfFile(data);
}
CloseHandle(hmap);
}
CloseHandle(hf);
}
sf->releaseInterface(loader);
}
return 0;
}
static void * writeImg(const ARGB32 *data, int w, int h, int *length, const wchar_t *ext)
{
if (!ext || (ext && !*ext)) return NULL;
if (*ext == L'.') ext++;
FOURCC imgwrite = svc_imageWriter::getServiceType();
int n = (int) WASABI_API_SVC->service_getNumServices(imgwrite);
for (int i=0; i<n; i++)
{
waServiceFactory *sf = WASABI_API_SVC->service_enumService(imgwrite,i);
if (sf)
{
svc_imageWriter * l = (svc_imageWriter*)sf->getInterface();
if (l)
{
if (wcsstr(l->getExtensions(),ext))
{
void* ret = l->convert(data,32,w,h,length);
sf->releaseInterface(l);
return ret;
}
sf->releaseInterface(l);
}
}
}
return NULL;
}
static int writeFile(const wchar_t *file, const void * data, int length)
{
FILE *f=_wfopen(file,L"wb");
if (!f) return ALBUMART_FAILURE;
if (fwrite(data,length,1,f) != 1)
{
fclose(f);
return ALBUMART_FAILURE;
}
fclose(f);
return ALBUMART_SUCCESS;
}
static void UpdateArtItemFrame(HWND hwndDlg)
{
EditArtItem * e = getCurrentArtItem(hwndDlg);
if (e)
{
wchar_t type[100] = {0}, buf[100] = {0}, *uiType = 0;
int sel = SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_GETCURSEL,0,0);
SendDlgItemMessageW(hwndDlg,IDC_ARTLIST,LB_GETTEXT,sel,(LPARAM)type);
if (e->mimetype)
{
uiType = wcschr(e->mimetype, L'/');
if (uiType && *uiType)
{
uiType++;
}
}
int origin[] = {IDS_ORIGIN_NONE, IDS_ORIGIN_EMBEDDED, IDS_ORIGIN_ALBUM_MATCH, IDS_ORIGIN_NFO,
IDS_ORIGIN_COVER_MATCH, IDS_ORIGIN_FOLDER_MATCH, IDS_ORIGIN_FRONT_MATCH, IDS_ORIGIN_ARTWORK_MATCH};
StringCchPrintfW(caption,128,getStringW(IDS_ARTWORK_DETAILS, NULL, 0), type, e->w, e->h,
// TODO: review what we set as the type for this from pasting...
getStringW(origin[2/*ret*/], buf, sizeof(buf)), (uiType && *uiType ? uiType : e->mimetype));
SetDlgItemTextW(hwndDlg,IDC_STATIC_FRAME, caption);
}
else
{
SetDlgItemTextW(hwndDlg,IDC_STATIC_FRAME,getStringW(IDS_NO_IMAGE,0,0));
}
}
static void writeImageToFile(ARGB32 * img, int w, int h, const wchar_t *file)
{
int length=0;
void * data = writeImg(img,w,h,&length,wcsrchr(file,L'.'));
if (data)
{
writeFile(file,data,length);
WASABI_API_MEMMGR->sysFree(data);
}
}
static INT_PTR CALLBACK FileInfo_Artwork(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
EnableArtFrame(hwndDlg,FALSE);
// added 30 May 2012 as per email from Tejas w.r.t. to Rovi deal ending
// as doing this means we keep the placeholder incase of a change at a
// later time and without doing anything to require an translation updates
HWND wnd = GetDlgItem(hwndDlg, IDC_BUTTON_DOWNLOAD);
if (IsWindow(wnd)) DestroyWindow(wnd);
info_params * p = (info_params *)GetWindowLongPtrW(GetParent(hwndDlg),GWLP_USERDATA);
if (AGAVE_API_ALBUMART)
{
int w = 0, h = 0;
ARGB32 *bits = NULL;
if (AGAVE_API_ALBUMART->GetAlbumArt(p->filename, L"cover", &w, &h, &bits) == ALBUMART_SUCCESS)
{
WASABI_API_MEMMGR->sysFree(bits);
int i = SendDlgItemMessageW(hwndDlg,IDC_ARTLIST,LB_ADDSTRING,0,(LPARAM)getStringW(IDS_COVER,NULL,0));
SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_SETCURSEL,i,0);
PostMessageW(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_ARTLIST,LBN_SELCHANGE),0);
}
wchar_t *types = NULL;
#if 0 // benski> TODO:
if (AGAVE_API_ALBUMART->GetAlbumArtTypes(p->filename,&types) == ALBUMART_SUCCESS)
{
wchar_t *p = types;
int sel = 0;
while (p && *p)
{
int is_cover = (!_wcsicmp(p,L"cover") || !_wcsicmp(p,getStringW(IDS_COVER,NULL,0)));
int i = SendDlgItemMessageW(hwndDlg,IDC_ARTLIST,LB_ADDSTRING,0,(LPARAM)(is_cover?getStringW(IDS_COVER,NULL,0):p));
if (!_wcsicmp(p,L"cover"))
sel = i;
p += wcslen(p) + 1;
}
WASABI_API_MEMMGR->sysFree(types);
SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_SETCURSEL,sel,0);
PostMessageW(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_ARTLIST,LBN_SELCHANGE),0);
}
#endif
if (AGAVE_API_ALBUMART->GetValidAlbumArtTypes(p->filename,&types) == ALBUMART_SUCCESS)
{
wchar_t *p = types;
int sel = 0;
while (p && *p)
{
int is_cover = (!_wcsicmp(p,L"cover") || !_wcsicmp(p,getStringW(IDS_COVER,NULL,0)));
int i = SendDlgItemMessageW(hwndDlg,IDC_COMBO_ARTTYPE,CB_ADDSTRING,0,(LPARAM)(is_cover?getStringW(IDS_COVER,NULL,0):p));
if (is_cover || !_wcsicmp(p, config_artwork_filter))
sel = i;
p += wcslen(p) + 1;
}
WASABI_API_MEMMGR->sysFree(types);
SendDlgItemMessage(hwndDlg,IDC_COMBO_ARTTYPE,CB_SETCURSEL,sel,0);
}
}
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_ARTLIST:
if (HIWORD(wParam) == LBN_SELCHANGE)
{
info_params * p = (info_params *)GetWindowLongPtrW(GetParent(hwndDlg),GWLP_USERDATA);
wchar_t type[100]=L"";
int sel = SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_GETCURSEL,0,0);
if (sel == -1)
{
HBITMAP bmold = (HBITMAP)SendDlgItemMessage(hwndDlg,IDC_ARTHOLDER,STM_SETIMAGE,IMAGE_BITMAP,(LPARAM)0);
if (bmold) DeleteObject(bmold);
EnableArtFrame(hwndDlg,FALSE);
return 0;
}
EnableArtFrame(hwndDlg,TRUE);
SendDlgItemMessageW(hwndDlg,IDC_ARTLIST,LB_GETTEXT,sel,(LPARAM)type);
int w = 0, h = 0;
ARGB32 *bits = NULL;
if (AGAVE_API_ALBUMART && AGAVE_API_ALBUMART->GetAlbumArt(p->filename,(!_wcsicmp(type,getStringW(IDS_COVER,NULL,0))?L"cover":type),&w,&h,&bits) == ALBUMART_SUCCESS)
{
HBITMAP bm = getBitmap(bits,w,h,228,228,hwndDlg);
HBITMAP bmold = (HBITMAP)SendDlgItemMessage(hwndDlg,IDC_ARTHOLDER,STM_SETIMAGE,IMAGE_BITMAP,(LPARAM)bm);
if (bmold) DeleteObject(bmold);
wchar_t caption[128], *mimeType = 0, *uiType = 0;
int origin[] = {IDS_ORIGIN_NONE, IDS_ORIGIN_EMBEDDED, IDS_ORIGIN_ALBUM_MATCH, IDS_ORIGIN_NFO,
IDS_ORIGIN_COVER_MATCH, IDS_ORIGIN_FOLDER_MATCH, IDS_ORIGIN_FRONT_MATCH, IDS_ORIGIN_ARTWORK_MATCH};
int ret = AGAVE_API_ALBUMART->GetAlbumArtOrigin(p->filename,(!_wcsicmp(type,getStringW(IDS_COVER,NULL,0))?L"cover":type), &mimeType);
if (mimeType)
{
uiType = wcschr(mimeType, L'/');
if (uiType && *uiType)
{
uiType++;
}
}
EditArtItem *e = new EditArtItem(bits,w,h);
if (e)
{
size_t len = 0;
ARGB32 *data = NULL;
if (AGAVE_API_ALBUMART->GetAlbumArtData(p->filename, L"cover", (void**)&data, &len, NULL) == ALBUMART_SUCCESS)
{
e->data = data;
e->datalen = len;
}
wchar_t mime[32] = {L"image/jpeg"};
if (uiType && *uiType)
{
StringCchPrintfW(mime, 32, L"image/%s", uiType);
}
else if(mimeType && *mimeType && _wcsicmp(mimeType, L"image/jpeg"))
{
lstrcpynW(mime, mimeType, 32);
}
if (mime && *mime) e->mimetype = _wcsdup(mime);
EditArtItem *eold = (EditArtItem *)SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_GETITEMDATA,sel,0);
if (eold && eold != (EditArtItem *)-1) delete eold;
SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_SETITEMDATA,sel,(LPARAM)e);
}
wchar_t buf[100] = {0}, buf2[32] = {0};
StringCchPrintfW(caption,128,getStringW(IDS_ARTWORK_DETAILS, NULL, 0), type, w, h,
getStringW(origin[ret], buf, ARRAYSIZE(buf)),
(uiType && *uiType ? uiType :
(mimeType && *mimeType ? mimeType :
getStringW(IDS_UNKNOWN_MIME, buf2, ARRAYSIZE(buf2)))));
SetDlgItemTextW(hwndDlg,IDC_STATIC_FRAME, caption);
WASABI_API_MEMMGR->sysFree(mimeType);
}
}
break;
// disabled 30 May 2012 as per email from Tejas w.r.t. to Rovi deal ending
#if 0
case IDC_BUTTON_DOWNLOAD:
{
wchar_t artist[256]=L"";
wchar_t album[256]=L"";
info_params * p = (info_params *)GetWindowLongPtrW(GetParent(hwndDlg),GWLP_USERDATA);
GetDlgItemTextW(p->tabs[0],IDC_ALBUM_ARTIST,artist,256);
if (!artist[0]) GetDlgItemTextW(p->tabs[0],IDC_ARTIST,artist,256);
GetDlgItemTextW(p->tabs[0],IDC_ALBUM,album,256);
artFetchData d = {sizeof(d),hwndDlg,artist,album,0};
int r = (int)SendMessageW(hMainWindow,WM_WA_IPC,(LPARAM)&d,IPC_FETCH_ALBUMART);
if (r == -2) break; // cancel all was pressed
if (r == 0 && d.imgData && d.imgDataLen) // success
{
if (AddNewArtItem(hwndDlg))
{
bool success=false;
EditArtItem *e = getCurrentArtItem(hwndDlg);
if (e)
{
int w=0,h=0;
ARGB32* data = decompressImage(d.imgData,d.imgDataLen,&w,&h);
if (data)
{
if (e->bits)
{
WASABI_API_MEMMGR->sysFree(e->bits);
e->bits = 0;
}
if (e->data)
{
WASABI_API_MEMMGR->sysFree(e->data);
e->data = 0;
e->datalen = 0;
}
e->bits = data;
e->w = w;
e->h = h;
if (e->data) WASABI_API_MEMMGR->sysFree(e->data);
e->data = d.imgData;
d.imgData = 0;
e->datalen = d.imgDataLen;
if (e->mimetype) free(e->mimetype);
e->mimetype = _wcsdup(L"jpg");
e->dirty = true;
HBITMAP dispbm = getBitmap(e->bits,e->w,e->h,228,228,hwndDlg);
HBITMAP bmold = (HBITMAP)SendDlgItemMessage(hwndDlg,IDC_ARTHOLDER,STM_SETIMAGE,IMAGE_BITMAP,(LPARAM)dispbm);
if (bmold) DeleteObject(bmold);
success=true;
}
}
if (!success)
{
// fail, remove :(
int sel = SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_GETCURSEL,0,0);
if (sel == -1)
return 0;
if (e) delete e;
SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_DELETESTRING,sel,0);
EnableArtFrame(hwndDlg,0);
}
}
if (d.imgData)
WASABI_API_MEMMGR->sysFree(d.imgData);
}
}
break;
#endif
case IDC_BUTTON_LOAD:
if (AddNewArtItem(hwndDlg))
{
if (!FileInfo_Artwork(hwndDlg,WM_COMMAND,IDC_BUTTON_CHANGE,0))
{
// fail, remove :(
int sel = SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_GETCURSEL,0,0);
if (sel == -1)
return 0;
EditArtItem * e = getCurrentArtItem(hwndDlg);
if (e) delete e;
SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_DELETESTRING,sel,0);
EnableArtFrame(hwndDlg,0);
}
}
break;
case IDC_BUTTON_CHANGE:
{
EditArtItem *e = getCurrentArtItem(hwndDlg);
if (!e) break;
info_params * p = (info_params *)GetWindowLongPtrW(GetParent(hwndDlg),GWLP_USERDATA);
wchar_t file[1024]=L"", folder[MAX_PATH]=L"";
OPENFILENAMEW fn = {sizeof(OPENFILENAMEW),0};
// set the ofd to the current file's folder
lstrcpynW(folder,p->filename,MAX_PATH);
PathRemoveFileSpecW(folder);
PathAddBackslashW(folder);
fn.lpstrInitialDir = folder;
fn.hwndOwner = hwndDlg;
fn.lpstrFile = file;
fn.nMaxFile = 1024;
static wchar_t fileExtensionsString[MAX_PATH] = {0};
if(!fileExtensionsString[0])
{
getStringW(IDS_IMAGE_FILES,fileExtensionsString,MAX_PATH);
wchar_t *temp=fileExtensionsString+lstrlenW(fileExtensionsString) + 1;
// query the available image loaders and build it against the supported formats
FOURCC imgload = svc_imageLoader::getServiceType();
int n = (int) WASABI_API_SVC->service_getNumServices(imgload);
for (int i=0; i<n; i++)
{
waServiceFactory *sf = WASABI_API_SVC->service_enumService(imgload,i);
if (sf)
{
svc_imageLoader * l = (svc_imageLoader*)sf->getInterface();
if (l)
{
wchar_t *tests[] = {L"*.jpg",L"*.jpeg",L"*.png",L"*.gif",L"*.bmp"};
for(int i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
{
if (l->isMine(tests[i]))
{
StringCchCatW(temp,MAX_PATH,tests[i]);
StringCchCatW(temp,MAX_PATH,L";");
}
}
sf->releaseInterface(l);
}
}
}
*(temp = temp + lstrlenW(temp) + 1) = 0;
}
fn.lpstrFilter = fileExtensionsString;
fn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOVALIDATE;
if (GetOpenFileNameW(&fn))
{
int len = 0, w = 0, h = 0;
wchar_t * mime = 0;
// TODO: benski> save original bits and mime type
// UPDATE: will only grab the raw data for jpeg files at the moment
ARGB32 * data = 0, * bits = loadImgFromFile(file,&len,&w,&h,&mime,&data);
if (bits)
{
if (e->bits)
{
WASABI_API_MEMMGR->sysFree(e->bits);
e->bits = 0;
}
if (e->data)
{
WASABI_API_MEMMGR->sysFree(e->data);
e->data = 0;
e->datalen = 0;
}
e->bits = bits;
e->w = w;
e->h = h;
if (data)
{
e->data = data;
e->datalen = len;
}
if (e->mimetype) free(e->mimetype);
e->mimetype = mime;
e->dirty = true;
HBITMAP dispbm = getBitmap(e->bits,e->w,e->h,228,228,hwndDlg);
HBITMAP bmold = (HBITMAP)SendDlgItemMessage(hwndDlg,IDC_ARTHOLDER,STM_SETIMAGE,IMAGE_BITMAP,(LPARAM)dispbm);
if (bmold) DeleteObject(bmold);
return 1;
}
}
break;
}
case IDC_BUTTON_SAVEAS:
{
EditArtItem *e = getCurrentArtItem(hwndDlg);
if (!e) break;
info_params * p = (info_params *)GetWindowLongPtrW(GetParent(hwndDlg),GWLP_USERDATA);
wchar_t file[1024]=L"", folder[MAX_PATH]=L"";
OPENFILENAMEW fn = {sizeof(OPENFILENAMEW),0};
// set the ofd to the current file's folder
lstrcpynW(folder,p->filename,MAX_PATH);
PathRemoveFileSpecW(folder);
PathAddBackslashW(folder);
fn.lpstrInitialDir = folder;
fn.hwndOwner = hwndDlg;
fn.lpstrFile = file;
fn.nMaxFile = 1020;
static wchar_t *mimes[4] = {0};
wchar_t *tests[] = {L"*.jpg",L"*.png",L"*.gif",L"*.bmp"};
static int tests_idx[4] = {0,1,2,3}, tests_run = 0, last_filter = -1;
static wchar_t filter[1024] = {0}, *sff = filter;
if(!tests_run)
{
int def_idx = 0;
tests_run = 1;
FOURCC imgload = svc_imageLoader::getServiceType();
int n = (int) WASABI_API_SVC->service_getNumServices(imgload);
for (int i = 0, j = 0; i < n; i++)
{
waServiceFactory *sf = WASABI_API_SVC->service_enumService(imgload,i);
if (sf)
{
svc_imageLoader * l = (svc_imageLoader*)sf->getInterface();
if (l)
{
int tests_str[] = {IDS_JPEG_FILE,IDS_PNG_FILE,IDS_GIF_FILE,IDS_BMP_FILE};
size_t size = 1024;
for(int k = 0; k < ARRAYSIZE(tests); k++)
{
if (l->isMine(tests[k]))
{
if (!k) def_idx = tests_idx[j] + 1;
tests_idx[j] = k;
mimes[j] = _wcsdup(l->mimeType());
j++;
int len = 0;
getStringW(tests_str[k],sff,size);
size-=(len = lstrlenW(sff)+1);
sff+=len;
lstrcpynW(sff,tests[k], (int)size);
if (!k) lstrcatW(sff, L";*.jpeg");
size-=(len = lstrlenW(sff)+1);
sff+=len;
}
}
sf->releaseInterface(l);
}
}
}
last_filter = _r_i("art_flt",last_filter);
if (last_filter == -1) last_filter = def_idx;
}
fn.lpstrFilter = filter;
fn.nFilterIndex = last_filter; // default to *.jpg / last filter
fn.Flags = OFN_OVERWRITEPROMPT;
if (GetSaveFileNameW(&fn))
{
int l = (int) wcslen(file);
if (l>4 && file[l-4]==L'.'); // we have an extention
else StringCchCatW(file,1024,tests[tests_idx[fn.nFilterIndex-1]]+1); // map to the extension to use
// where possible see if we can just save the image data without conversion
if (e->data && e->mimetype && !_wcsicmp(mimes[fn.nFilterIndex-1], e->mimetype))
{
writeFile(file, e->data, (int)e->datalen);
}
else
{
// though if we're not sure or it is a different format from what is stored
// then we'll need to convert (not nice to do) and hope it doesn't fail...
writeImageToFile(e->bits,e->w,e->h,file);
}
}
last_filter = fn.nFilterIndex;
_w_i("art_flt",last_filter);
break;
}
case IDC_BUTTON_COPY:
{
EditArtItem *e = getCurrentArtItem(hwndDlg);
if (!e) break;
if (!OpenClipboard(hwndDlg)) break;
EmptyClipboard();
HBITMAP bm = getBitmap(e->bits,e->w,e->h,hwndDlg);
SetClipboardData(CF_BITMAP,bm);
CloseClipboard();
DeleteObject(bm);
break;
}
case IDC_BUTTON_PASTENEW:
if (AddNewArtItem(hwndDlg))
{
if (!FileInfo_Artwork(hwndDlg,WM_COMMAND,IDC_BUTTON_PASTE,0))
{
// fail, remove :(
int sel = SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_GETCURSEL,0,0);
if (sel == -1)
return 0;
EditArtItem * e = getCurrentArtItem(hwndDlg);
if (e) delete e;
SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_DELETESTRING,sel,0);
EnableArtFrame(hwndDlg,0);
}
}
UpdateArtItemFrame(hwndDlg);
break;
case IDC_BUTTON_PASTE:
{
EditArtItem *e = getCurrentArtItem(hwndDlg);
if (!e) break;
if (!OpenClipboard(hwndDlg)) break;
HBITMAP bm = (HBITMAP)GetClipboardData(CF_BITMAP);
if (bm)
{
GetSize(bm,e->w,e->h,hwndDlg);
BITMAPINFO info={0};
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = e->w;
info.bmiHeader.biHeight = -e->h;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = 32;
info.bmiHeader.biCompression = BI_RGB;
HDC dc = GetDC(hwndDlg);
if (e->bits)
{
WASABI_API_MEMMGR->sysFree(e->bits);
e->bits = 0;
}
if (e->data)
{
WASABI_API_MEMMGR->sysFree(e->data);
e->data = 0;
e->datalen = 0;
}
e->bits = (ARGB32*)WASABI_API_MEMMGR->sysMalloc(e->w*e->h*sizeof(ARGB32));
if (e->mimetype) free(e->mimetype);
e->mimetype = _wcsdup(L"image/jpeg");
GetDIBits(dc,bm,0,e->h,e->bits,&info,DIB_RGB_COLORS);
ReleaseDC(hwndDlg,dc);
e->dirty=true;
HBITMAP dispbm = getBitmap(e->bits,e->w,e->h,228,228,hwndDlg);
HBITMAP bmold = (HBITMAP)SendDlgItemMessage(hwndDlg,IDC_ARTHOLDER,STM_SETIMAGE,IMAGE_BITMAP,(LPARAM)dispbm);
if (bmold) DeleteObject(bmold);
CloseClipboard();
UpdateArtItemFrame(hwndDlg);
return 1;
}
CloseClipboard();
break;
}
case IDC_BUTTON_DELETE:
{
wchar_t buf[1024] = {0};
getStringW(IDS_ARTDELETE,buf,1024);
if (MessageBoxW(hwndDlg,buf,getStringW(IDS_AREYOUSURE,0,0),MB_YESNO|MB_ICONQUESTION) != IDYES) break;
info_params * p = (info_params *)GetWindowLongPtrW(GetParent(hwndDlg),GWLP_USERDATA);
EditArtItem *e = getCurrentArtItem(hwndDlg);
if (!e) break;
int sel = SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_GETCURSEL,0,0);
buf[0]=0;
SendDlgItemMessageW(hwndDlg,IDC_ARTLIST,LB_GETTEXT,sel,(LPARAM)buf);
AGAVE_API_ALBUMART->DeleteAlbumArt(p->filename,(!_wcsicmp(buf,getStringW(IDS_COVER,NULL,0))?L"cover":buf));
HBITMAP bmold = (HBITMAP)SendDlgItemMessage(hwndDlg,IDC_ARTHOLDER,STM_SETIMAGE,IMAGE_BITMAP,(LPARAM)0);
if (bmold) DeleteObject(bmold);
delete e;
SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_DELETESTRING,sel,0);
EnableArtFrame(hwndDlg,0);
break;
}
case IDOK:
{
info_params * p = (info_params *)GetWindowLongPtrW(GetParent(hwndDlg),GWLP_USERDATA);
int l = SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_GETCOUNT,0,0);
for (int i=0; i<l; i++)
{
EditArtItem *e = (EditArtItem *)SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_GETITEMDATA,i,0);
if (e && e->dirty)
{
wchar_t buf[1024] = {0};
SendDlgItemMessageW(hwndDlg,IDC_ARTLIST,LB_GETTEXT,i,(LPARAM)buf);
if (e->data)
{
AGAVE_API_ALBUMART->SetAlbumArt(p->filename,(!_wcsicmp(buf,getStringW(IDS_COVER,NULL,0))?L"cover":buf),
0,0,e->data,e->datalen,e->mimetype);
}
else
{
AGAVE_API_ALBUMART->SetAlbumArt(p->filename,(!_wcsicmp(buf,getStringW(IDS_COVER,NULL,0))?L"cover":buf),
e->w,e->h,e->bits,0,0);
}
}
}
}
break;
}
break;
case WM_DESTROY:
{
HBITMAP bmold = (HBITMAP)SendDlgItemMessage(hwndDlg,IDC_ARTHOLDER,STM_GETIMAGE,IMAGE_BITMAP,0);
if (bmold) DeleteObject(bmold);
int l = SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_GETCOUNT,0,0);
for (int i=0; i<l; i++)
{
EditArtItem *e = (EditArtItem *)SendDlgItemMessage(hwndDlg,IDC_ARTLIST,LB_GETITEMDATA,i,0);
if (e && e != (EditArtItem *)-1) delete e;
}
GetDlgItemTextW(hwndDlg, IDC_COMBO_ARTTYPE, config_artwork_filter, ARRAYSIZE(config_artwork_filter));
}
break;
}
return 0;
}
static int checkEditInfoClick(HWND hwndDlg, POINT p, int item, int check)
{
RECT r = {0};
GetWindowRect(GetDlgItem(hwndDlg, item), &r);
ScreenToClient(hwndDlg, (LPPOINT)&r);
ScreenToClient(hwndDlg, (LPPOINT)&r.right);
if (PtInRect(&r, p) && !IsDlgButtonChecked(hwndDlg, check))
{
CheckDlgButton(hwndDlg, check, TRUE);
if (item == IDC_COMBO_RATING) SendDlgItemMessage(hwndDlg, IDC_COMBO_RATING, CB_SHOWDROPDOWN, TRUE, 0);
EnableWindow(GetDlgItem(hwndDlg, item), TRUE);
EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE);
PostMessageW(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, item), (LPARAM)TRUE);
return 1;
}
return 0;
}
// sets part and parts to -1 or 0 on fail/missing (e.g. parts will be -1 on "1", but 0 on "1/")
void ParseIntSlashInt(wchar_t *string, int *part, int *parts)
{
*part = -1;
*parts = -1;
if (string && string[0])
{
*part = _wtoi(string);
while (string && *string && *string != '/')
{
string++;
}
if (string && *string == '/')
{
string++;
*parts = _wtoi(string);
}
}
}
typedef struct
{
mlQueryStructW item;
int idx;
int ml;
} queryStructW;
std::vector<queryStructW*> queryList;
static int got_local_ml = -1;
static size_t m_upd_nb, m_stopped, m_upd_nb_all, m_upd_nb_cur;
#define MAKESAFE(x) ((x)?(x):L"")
static void FreeQueryList()
{
for ( queryStructW *query : queryList )
{
free( query->item.query );
if ( query->ml )
sendMlIpc( ML_IPC_DB_FREEQUERYRESULTSW, (WPARAM)query );
else
freeRecordList( &query->item.results );
free( query );
}
queryList.clear();
}
void CHECK_AND_COPY(itemRecordW *song, HWND hwndParent, int IDCHECK, int ID, wchar_t *&item)
{
if (IsDlgButtonChecked(hwndParent, IDCHECK)) {
wchar_t blah[2048];
GetDlgItemTextW(hwndParent, ID, blah, 2048);
if (wcscmp(MAKESAFE(item), blah))
{
free(item);
item = _wcsdup(blah);
}
}
}
static void CHECK_AND_COPY_EXTENDED(itemRecordW *song, HWND hwndParent, int IDCHECK, int ID, const wchar_t *name)
{
if (IsDlgButtonChecked(hwndParent, IDCHECK)) {
wchar_t blah[2048];
GetDlgItemTextW(hwndParent, ID, blah, 2048);
wchar_t *oldData = getRecordExtendedItem(song, name);
if (wcscmp(MAKESAFE(oldData), blah)) {
setRecordExtendedItem(song, name, blah);
}
}
}
static INT_PTR CALLBACK updateFiles_dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
SetWindowTextW(hwndDlg, getStringW(IDS_UPDATING_FILES, NULL, 0));
SetWindowLong(GetDlgItem(hwndDlg, 1008), GWL_STYLE, (GetWindowLongW(GetDlgItem(hwndDlg, 1008), GWL_STYLE)&~SS_CENTER) | SS_LEFTNOWORDWRAP);
SetTimer(hwndDlg, 0x123, 30, NULL);
m_upd_nb = 0;
m_stopped = 0;
SendDlgItemMessage(hwndDlg, 1007, PBM_SETRANGE, 0, MAKELPARAM(0, m_upd_nb_all));
m_upd_nb_cur = 0;
break;
}
case WM_TIMER:
if (wParam == 0x123 && !m_stopped)
{
unsigned int start_t = GetTickCount();
again:
{
if (m_upd_nb >= queryList.size())
{
//done
if (got_local_ml == 1) sendMlIpc(ML_IPC_DB_SYNCDB, 0);
EndDialog(hwndDlg, 1);
break;
}
int i = (int) m_upd_nb++;
itemRecordW *mlsong = &queryList[i]->item.results.Items[0];
itemRecordW copy;
itemRecordW *song = &copy;
copyRecord(&copy, mlsong);
HWND hwndParent = GetParent(hwndDlg);
wchar_t stattmp[512] = {0};
wchar_t *p = scanstr_backW(song->filename, L"\\", song->filename - 1) + 1;
wsprintfW(stattmp, getStringW(IDS_UPDATING_X, NULL, 0), p);
SetDlgItemTextW(hwndDlg, 1008, stattmp);
SendDlgItemMessage(hwndDlg, 1007, PBM_SETPOS, m_upd_nb_cur, 0);
m_upd_nb_cur++;
int updtagz = !!IsDlgButtonChecked(hwndParent, 1052);
CHECK_AND_COPY(song, hwndParent, IDC_CHECK_ARTIST, IDC_EDIT_ARTIST, song->artist);
CHECK_AND_COPY(song, hwndParent, IDC_CHECK_TITLE, IDC_EDIT_TITLE, song->title);
CHECK_AND_COPY(song, hwndParent, IDC_CHECK_ALBUM, IDC_EDIT_ALBUM, song->album);
CHECK_AND_COPY(song, hwndParent, IDC_CHECK_COMMENT, IDC_EDIT_COMMENT, song->comment);
CHECK_AND_COPY(song, hwndParent, IDC_CHECK_GENRE, IDC_EDIT_GENRE, song->genre);
CHECK_AND_COPY(song, hwndParent, IDC_CHECK_ALBUMARTIST, IDC_EDIT_ALBUMARTIST, song->albumartist);
CHECK_AND_COPY(song, hwndParent, IDC_CHECK_PUBLISHER, IDC_EDIT_PUBLISHER, song->publisher);
CHECK_AND_COPY(song, hwndParent, IDC_CHECK_COMPOSER, IDC_EDIT_COMPOSER, song->composer);
CHECK_AND_COPY(song, hwndParent, IDC_CHECK_CATEGORY, IDC_EDIT_CATEGORY, song->category);
CHECK_AND_COPY_EXTENDED(song, hwndParent, IDC_CHECK_DIRECTOR, IDC_EDIT_DIRECTOR, L"director");
CHECK_AND_COPY_EXTENDED(song, hwndParent, IDC_CHECK_PRODUCER, IDC_EDIT_PRODUCER, L"producer");
CHECK_AND_COPY_EXTENDED(song, hwndParent, IDC_CHECK_PODCAST_CHANNEL, IDC_EDIT_PODCAST_CHANNEL, L"podcastchannel");
#define CHECK_AND_COPY_EXTENDED_PC(IDCHECK, field, name) \
wchar_t blah[2048] = {0}; StringCchPrintfW(blah, 2048, L"%d", (IsDlgButtonChecked(hwndParent, IDCHECK) == BST_CHECKED));\
wchar_t *oldData = getRecordExtendedItem(song, name);\
if (wcscmp(MAKESAFE(oldData), blah)) { setRecordExtendedItem(song, name, blah); }
CHECK_AND_COPY_EXTENDED_PC(IDC_CHECK_PODCAST, MAINTABLE_ID_ISPODCAST, L"ispodcast");
if (IsDlgButtonChecked(hwndParent, IDC_CHECK_TRACK))
{
wchar_t blah[64] = {0};
GetDlgItemTextW(hwndParent, IDC_EDIT_TRACK, blah, 64);
int track, tracks;
ParseIntSlashInt(blah, &track, &tracks);
if (tracks <= 0) tracks = -1;
if (track <= 0) track = -1;
if (song->track != track || song->tracks != tracks)
{
song->track = track;
song->tracks = tracks;
}
}
if (IsDlgButtonChecked(hwndParent, IDC_CHECK_DISC))
{
wchar_t blah[64] = {0};
GetDlgItemTextW(hwndParent, IDC_EDIT_DISC, blah, 64);
int disc, discs;
ParseIntSlashInt(blah, &disc, &discs);
if (discs <= 0) discs = -1;
if (disc <= 0) disc = -1;
if (song->disc != disc || song->discs != discs)
{
song->disc = disc;
song->discs = discs;
}
}
if (IsDlgButtonChecked(hwndParent, IDC_CHECK_YEAR))
{
char blah[64] = {0};
GetDlgItemTextA(hwndParent, IDC_EDIT_YEAR, blah, 64);
int n = atoi(blah);
if (n <= 0) n = -1;
if (song->year != n) song->year = n;
}
if (IsDlgButtonChecked(hwndParent, IDC_CHECK_BPM))
{
char blah[64] = {0};
GetDlgItemTextA(hwndParent, IDC_EDIT_BPM, blah, 64);
int n = atoi(blah);
if (n <= 0) n = -1;
if (song->bpm != n) song->bpm = n;
}
if (IsDlgButtonChecked(hwndParent, IDC_CHECK_RATING))
{
int n = SendDlgItemMessage(hwndParent, IDC_COMBO_RATING, CB_GETCURSEL, 0, 0), rating = -1;
if (n != CB_ERR && (n >= 0 && n < 5)) rating = 5 - n;
if (song->rating != rating) song->rating = rating;
}
// no need to send this if there's no ml present
if (got_local_ml == 1) sendMlIpc((!config_upd_mode ? ML_IPC_DB_ADDORUPDATEITEMW : ML_IPC_DB_UPDATEITEMW), (WPARAM)song);
if (updtagz || !got_local_ml)
{
m_stopped = 1;
retry:
if (in_set_extended_fileinfoW(song->filename, L"title", song->title)) // if this returns 0, then this format doesnt even support extended
{
in_set_extended_fileinfoW(song->filename, L"artist", song->artist);
in_set_extended_fileinfoW(song->filename, L"album", song->album);
in_set_extended_fileinfoW(song->filename, L"comment", song->comment);
in_set_extended_fileinfoW(song->filename, L"genre", song->genre);
wchar_t buf[32] = {0};
if (song->track > 0)
{
if (song->tracks > 0)
wsprintfW(buf, L"%d/%d", song->track, song->tracks);
else
wsprintfW(buf, L"%d", song->track);
}
else buf[0] = 0;
in_set_extended_fileinfoW(song->filename, L"track", buf);
if (song->year > 0) wsprintfW(buf, L"%d", song->year);
else buf[0] = 0;
in_set_extended_fileinfoW(song->filename, L"year", buf);
if (song->disc > 0)
{
if (song->discs > 0)
wsprintfW(buf, L"%d/%d", song->disc, song->discs);
else
wsprintfW(buf, L"%d", song->disc);
}
else buf[0] = 0;
in_set_extended_fileinfoW(song->filename, L"disc", buf);
if (song->rating > 0) wsprintfW(buf, L"%d", song->rating);
else buf[0] = 0;
if (!in_set_extended_fileinfoW(song->filename, L"rating", buf))
{
// for ratings, try using the library just to cover all bases for format types
file_set_ratingW rate = {0};
rate.fileName = song->filename;
rate.newRating = song->rating;
sendMlIpc(ML_IPC_SET_FILE_RATINGW, (WPARAM)&rate);
}
if (song->bpm > 0) wsprintfW(buf, L"%d", song->bpm);
else buf[0] = 0;
in_set_extended_fileinfoW(song->filename, L"bpm", buf);
in_set_extended_fileinfoW(song->filename, L"albumartist", song->albumartist);
in_set_extended_fileinfoW(song->filename, L"publisher", song->publisher);
in_set_extended_fileinfoW(song->filename, L"composer", song->composer);
in_set_extended_fileinfoW(song->filename, L"category", song->category);
in_set_extended_fileinfoW(song->filename, L"director", getRecordExtendedItem(song, L"director"));
in_set_extended_fileinfoW(song->filename, L"producer", getRecordExtendedItem(song, L"producer"));
if (in_write_extended_fileinfo() == 0)
{
wchar_t tmp[1024] = {0};
wsprintfW(tmp, getStringW(IDS_ERROR_UPDATING_FILE, NULL, 0), song->filename);
int ret = MessageBoxW(hwndDlg, tmp, getStringW(IDS_INFO_UPDATING_ERROR, NULL, 0), MB_RETRYCANCEL);
if (ret == IDRETRY) goto retry;
if (ret == IDCANCEL)
{
EndDialog(hwndDlg, 0);
freeRecord(&copy);
break;
}
}
}
int x = queryList[i]->idx;
SendMessageW(hMainWindow, WM_WA_IPC, (WPARAM)song->filename, IPC_FILE_TAG_MAY_HAVE_UPDATEDW);
PlayList_setitem(x, song->filename, PlayList_gettitle(song->filename, 1));
PlayList_setlastlen(x);
m_stopped = 0;
}
freeRecord(&copy);
}
if (GetTickCount() - start_t < 30) goto again;
}
break;
case WM_COMMAND:
if (LOWORD(wParam) == IDCANCEL)
{
EndDialog(hwndDlg, 0);
}
break;
}
return FALSE;
}
INT_PTR CALLBACK EditInfo(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
wchar_t *last_artist = NULL, *last_title = NULL, *last_album = NULL, *last_genre = NULL,
*last_comment = NULL, *last_albumartist = NULL, *last_composer = NULL,
*last_publisher = NULL, *last_ispodcast = NULL, *last_podcastchannel = NULL;
const wchar_t *last_category = NULL, *last_director = NULL, *last_producer = NULL;
int last_year = -1, last_track = -1, last_disc = -1, last_discs = -1, last_tracks = -1,
last_bpm = -1, last_rating = -1, disable_artist = 0, disable_title = 0,
disable_album = 0, disable_genre = 0, disable_year = 0, disable_track = 0,
disable_comment = 0, disable_disc = 0, disable_albumartist = 0, disable_composer = 0,
disable_publisher = 0, disable_discs = 0, disable_tracks = 0, disable_category = 0,
disable_director = 0, disable_producer = 0, disable_bpm = 0, disable_rating = 0,
disable_ispodcast = 0, disable_podcastchannel = 0, nb = 0;
if (got_local_ml == -1)
{
got_local_ml = (got_ml ? !!GetModuleHandleW(L"ml_local.dll") : 0);
}
if (got_local_ml == 1)
{
if (GetPrivateProfileIntW(L"gen_ml_config", L"upd_tagz", 1, ML_INI_FILE))
CheckDlgButton(hwndDlg, 1052, BST_CHECKED);
}
else
ShowWindow(GetDlgItem(hwndDlg, 1052), SW_HIDE);
EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
SendDlgItemMessageW(hwndDlg, IDC_COMBO_RATING, CB_ADDSTRING, 0, (LPARAM)L"\u2605\u2605\u2605\u2605\u2605");
SendDlgItemMessageW(hwndDlg, IDC_COMBO_RATING, CB_ADDSTRING, 0, (LPARAM)L"\u2605\u2605\u2605\u2605");
SendDlgItemMessageW(hwndDlg, IDC_COMBO_RATING, CB_ADDSTRING, 0, (LPARAM)L"\u2605\u2605\u2605");
SendDlgItemMessageW(hwndDlg, IDC_COMBO_RATING, CB_ADDSTRING, 0, (LPARAM)L"\u2605\u2605");
SendDlgItemMessageW(hwndDlg, IDC_COMBO_RATING, CB_ADDSTRING, 0, (LPARAM)L"\u2605");
SendDlgItemMessageW(hwndDlg, IDC_COMBO_RATING, CB_ADDSTRING, 0, (LPARAM)getStringW(IDS_NO_RATING, NULL, 0));
FreeQueryList();
LPWSTR pszBuffer = (LPWSTR)calloc(TEXTBUFFER_MAX, sizeof(WCHAR));
if (pszBuffer)
{
for (int i = 0; i < PlayList_getlength(); i++)
{
wchar_t fn[FILENAME_SIZE] = {0};
if (PlayList_getselect2(i, fn))
{
if (!PathIsURLW(fn) || !_wcsnicmp(fn, L"cda://", 6))
{
queryStructW *query = (queryStructW *)calloc(1, sizeof(queryStructW));
if (query)
{
queryList.push_back(query);
query->item.query = _wcsdup(fn);
// hit the library where possible as it'll be faster (and more integrated)
if (got_local_ml == 1)
{
if (sendMlIpc(ML_IPC_DB_RUNQUERY_FILENAMEW, (WPARAM)query) == 1)
{
#define SAVE_LAST_STR(last, check, disable) if (!disable && check && check[0]) { if (!last) last = check; else if (wcscmp(check, last)) disable = 1; }
#define SAVE_LAST_INT(last, check, disable) if (!disable && check > 0) { if (last == -1) last = check; else if (last != check) disable = 1; }
SAVE_LAST_STR(last_artist, query->item.results.Items[0].artist, disable_artist);
SAVE_LAST_STR(last_title, query->item.results.Items[0].title, disable_title);
SAVE_LAST_STR(last_album, query->item.results.Items[0].album, disable_album);
SAVE_LAST_STR(last_comment, query->item.results.Items[0].comment, disable_comment);
SAVE_LAST_STR(last_genre, query->item.results.Items[0].genre, disable_genre);
SAVE_LAST_INT(last_year, query->item.results.Items[0].year, disable_year);
SAVE_LAST_INT(last_track, query->item.results.Items[0].track, disable_track);
SAVE_LAST_INT(last_tracks, query->item.results.Items[0].tracks, disable_tracks);
SAVE_LAST_INT(last_disc, query->item.results.Items[0].disc, disable_disc);
SAVE_LAST_INT(last_discs, query->item.results.Items[0].discs, disable_discs);
SAVE_LAST_INT(last_rating, query->item.results.Items[0].rating, disable_rating);
SAVE_LAST_INT(last_bpm, query->item.results.Items[0].bpm, disable_bpm);
SAVE_LAST_STR(last_albumartist, query->item.results.Items[0].albumartist, disable_albumartist);
SAVE_LAST_STR(last_composer, query->item.results.Items[0].composer, disable_composer);
SAVE_LAST_STR(last_publisher, query->item.results.Items[0].publisher, disable_publisher);
SAVE_LAST_STR(last_category, query->item.results.Items[0].category, disable_category);
#define SAVE_LAST_STR_EXTENDED(last, name, disable) if (!disable) { wchar_t *check = getRecordExtendedItem(&query->item.results.Items[0], name); if (check && check[0]) { if (!last) last = check; else if (wcscmp(check, last)) disable = 1; }};
SAVE_LAST_STR_EXTENDED(last_director, L"director", disable_director);
SAVE_LAST_STR_EXTENDED(last_producer, L"producer", disable_producer);
SAVE_LAST_STR_EXTENDED(last_podcastchannel, L"podcastchannel", disable_podcastchannel);
SAVE_LAST_STR_EXTENDED(last_ispodcast, L"ispodcast", disable_ispodcast);
nb++;
query->ml = 1;
query->idx = i;
}
else
{
goto non_ml;
}
}
else
{
non_ml:
allocRecordList(&query->item.results, 1, 0);
query->item.results.Items[0].filename = _wcsdup(fn);
#define SAVE_LAST_STR(last, check, disable) if (!disable && check && check[0]) { if (!last) last = check; else if (wcscmp(check, last)) disable = 1; }
#define SAVE_LAST_INT(last, check, disable) if (!disable && check > 0) { if (last == -1) last = check; else if (last != check) disable = 1; }
if (in_get_extended_fileinfoW(fn, L"artist", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].artist = _wcsdup(pszBuffer);
SAVE_LAST_STR(last_artist, query->item.results.Items[0].artist, disable_artist);
if (in_get_extended_fileinfoW(fn, L"title", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].title = _wcsdup(pszBuffer);
SAVE_LAST_STR(last_title, query->item.results.Items[0].title, disable_title);
if (in_get_extended_fileinfoW(fn, L"album", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].album = _wcsdup(pszBuffer);
SAVE_LAST_STR(last_album, query->item.results.Items[0].album, disable_album);
if (in_get_extended_fileinfoW(fn, L"comment", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].comment = _wcsdup(pszBuffer);
SAVE_LAST_STR(last_comment, query->item.results.Items[0].comment, disable_comment);
if (in_get_extended_fileinfoW(fn, L"genre", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].genre = _wcsdup(pszBuffer);
SAVE_LAST_STR(last_genre, query->item.results.Items[0].genre, disable_genre);
if (in_get_extended_fileinfoW(fn, L"year", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].year = _wtoi(pszBuffer);
SAVE_LAST_INT(last_year, query->item.results.Items[0].year, disable_year);
if (in_get_extended_fileinfoW(fn, L"track", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].track = _wtoi(pszBuffer);
SAVE_LAST_INT(last_track, query->item.results.Items[0].track, disable_track);
if (in_get_extended_fileinfoW(fn, L"tracks", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].tracks = _wtoi(pszBuffer);
SAVE_LAST_INT(last_tracks, query->item.results.Items[0].tracks, disable_tracks);
if (in_get_extended_fileinfoW(fn, L"disc", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].disc = _wtoi(pszBuffer);
SAVE_LAST_INT(last_disc, query->item.results.Items[0].disc, disable_disc);
if (in_get_extended_fileinfoW(fn, L"discs", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].discs = _wtoi(pszBuffer);
SAVE_LAST_INT(last_discs, query->item.results.Items[0].discs, disable_discs);
// for ratings, try using the library just to cover all bases for format types
if (in_get_extended_fileinfoW(fn, L"rating", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].rating = _wtoi(pszBuffer);
else
query->item.results.Items[0].rating = sendMlIpc(ML_IPC_GET_FILE_RATINGW, (WPARAM)fn);
SAVE_LAST_INT(last_rating, query->item.results.Items[0].rating, disable_rating);
if (in_get_extended_fileinfoW(fn, L"bpm", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].bpm = _wtoi(pszBuffer);
SAVE_LAST_INT(last_bpm, query->item.results.Items[0].bpm, disable_bpm);
if (in_get_extended_fileinfoW(fn, L"albumartist", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].albumartist = _wcsdup(pszBuffer);
SAVE_LAST_STR(last_albumartist, query->item.results.Items[0].albumartist, disable_albumartist);
if (in_get_extended_fileinfoW(fn, L"composer", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].composer = _wcsdup(pszBuffer);
SAVE_LAST_STR(last_composer, query->item.results.Items[0].composer, disable_composer);
if (in_get_extended_fileinfoW(fn, L"publisher", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].publisher = _wcsdup(pszBuffer);
SAVE_LAST_STR(last_publisher, query->item.results.Items[0].publisher, disable_publisher);
if (in_get_extended_fileinfoW(fn, L"category", pszBuffer, TEXTBUFFER_MAX))
query->item.results.Items[0].category = _wcsdup(pszBuffer);
SAVE_LAST_STR(last_category, query->item.results.Items[0].category, disable_category);
#define SAVE_LAST_STR_EXTENDED(last, name, disable) if (!disable) { wchar_t *check = getRecordExtendedItem(&query->item.results.Items[0], name); if (check && check[0]) { if (!last) last = check; else if (wcscmp(check, last)) disable = 1; }};
if (in_get_extended_fileinfoW(fn, L"director", pszBuffer, TEXTBUFFER_MAX))
{
setRecordExtendedItem(&query->item.results.Items[0], L"director", pszBuffer);
SAVE_LAST_STR_EXTENDED(last_director, L"director", disable_director);
}
if (in_get_extended_fileinfoW(fn, L"producer", pszBuffer, TEXTBUFFER_MAX))
{
setRecordExtendedItem(&query->item.results.Items[0], L"producer", pszBuffer);
SAVE_LAST_STR_EXTENDED(last_producer, L"producer", disable_producer);
}
// not available in non-ml setups
/*SAVE_LAST_STR_EXTENDED(last_podcastchannel, L"podcastchannel", disable_podcastchannel);
SAVE_LAST_STR_EXTENDED(last_ispodcast, L"ispodcast", disable_ispodcast);*/
nb++;
query->ml = 0;
query->idx = i;
}
}
}
}
}
free(pszBuffer);
}
if (!disable_artist && last_artist) SetDlgItemTextW(hwndDlg, IDC_EDIT_ARTIST, last_artist);
if (!disable_title && last_title) SetDlgItemTextW(hwndDlg, IDC_EDIT_TITLE, last_title);
if (!disable_album && last_album) SetDlgItemTextW(hwndDlg, IDC_EDIT_ALBUM, last_album);
if (!disable_comment && last_comment) SetDlgItemTextW(hwndDlg, IDC_EDIT_COMMENT, last_comment);
if (!disable_albumartist && last_albumartist) SetDlgItemTextW(hwndDlg, IDC_EDIT_ALBUMARTIST, last_albumartist);
if (!disable_composer && last_composer) SetDlgItemTextW(hwndDlg, IDC_EDIT_COMPOSER, last_composer);
if (!disable_publisher && last_publisher) SetDlgItemTextW(hwndDlg, IDC_EDIT_PUBLISHER, last_publisher);
if (!disable_genre && last_genre) SetDlgItemTextW(hwndDlg, IDC_EDIT_GENRE, last_genre);
if (!disable_category && last_category) SetDlgItemTextW(hwndDlg, IDC_EDIT_CATEGORY, last_category);
if (!disable_director && last_director) SetDlgItemTextW(hwndDlg, IDC_EDIT_DIRECTOR, last_director);
if (!disable_producer && last_producer) SetDlgItemTextW(hwndDlg, IDC_EDIT_PRODUCER, last_producer);
if (!disable_podcastchannel && last_podcastchannel) SetDlgItemTextW(hwndDlg, IDC_EDIT_PODCAST_CHANNEL, last_podcastchannel);
if (!disable_ispodcast && last_ispodcast)
{
CheckDlgButton(hwndDlg, IDC_CHECK_PODCAST, (_wtoi(last_ispodcast) == 1 ? BST_CHECKED : BST_UNCHECKED));
}
if (!disable_year && last_year > 0)
{
wchar_t tmp[64] = {0};
wsprintfW(tmp, L"%d", last_year);
SetDlgItemTextW(hwndDlg, IDC_EDIT_YEAR, tmp);
}
if (!disable_bpm && last_bpm > 0)
{
wchar_t tmp[64] = {0};
wsprintfW(tmp, L"%d", last_bpm);
SetDlgItemTextW(hwndDlg, IDC_EDIT_BPM, tmp);
}
if (!disable_rating)
{
if (last_rating > 0 && last_rating <= 5)
{
SendDlgItemMessage(hwndDlg, IDC_COMBO_RATING, CB_SETCURSEL, 5 - last_rating, 0);
}
else SendDlgItemMessage(hwndDlg, IDC_COMBO_RATING, CB_SETCURSEL, 5, 0);
}
if (!disable_track && last_track > 0 && !disable_tracks)
{
wchar_t tmp[64] = {0};
if (!disable_tracks && last_tracks > 0)
wsprintfW(tmp, L"%d/%d", last_track, last_tracks);
else
wsprintfW(tmp, L"%d", last_track);
SetDlgItemTextW(hwndDlg, IDC_EDIT_TRACK, tmp);
}
if (!disable_disc && last_disc > 0
&& !disable_discs)
{
wchar_t tmp[64] = {0};
if (!disable_discs && last_discs > 0)
wsprintfW(tmp, L"%d/%d", last_disc, last_discs);
else
wsprintfW(tmp, L"%d", last_disc);
SetDlgItemTextW(hwndDlg, IDC_EDIT_DISC, tmp);
}
wchar_t tmp[512] = {0};
wsprintfW(tmp, getStringW((nb==1?IDS_X_ITEM_SELECTED:IDS_X_ITEMS_SELECTED), NULL, 0), nb);
SetDlgItemTextW(hwndDlg, 1051, tmp);
if (!nb)
{
EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
ShowWindow(GetDlgItem(hwndDlg, 1052), SW_HIDE);
}
// show edit info window and restore last position as applicable
POINT pt = {editinfo_rect.left, editinfo_rect.top};
if (!windowOffScreen(hwndDlg, pt) && !IsWindowVisible(hwndDlg))
SetWindowPos(hwndDlg, HWND_TOP, editinfo_rect.left, editinfo_rect.top, 0, 0, SWP_NOSIZE | SWP_NOSENDCHANGING);
}
return 1;
case WM_COMMAND:
#define HANDLE_CONTROL(item, check) { int enabled = IsDlgButtonChecked(hwndDlg, check); EnableWindow(GetDlgItem(hwndDlg, item), enabled); EnableWindow(GetDlgItem(hwndDlg, IDOK), enabled); }
switch (LOWORD(wParam))
{
case IDC_CHECK_ARTIST: HANDLE_CONTROL(IDC_EDIT_ARTIST, IDC_CHECK_ARTIST); break;
case IDC_CHECK_TITLE: HANDLE_CONTROL(IDC_EDIT_TITLE, IDC_CHECK_TITLE); break;
case IDC_CHECK_ALBUM: HANDLE_CONTROL(IDC_EDIT_ALBUM, IDC_CHECK_ALBUM); break;
case IDC_CHECK_COMMENT: HANDLE_CONTROL(IDC_EDIT_COMMENT, IDC_CHECK_COMMENT); break;
case IDC_CHECK_ALBUMARTIST: HANDLE_CONTROL(IDC_EDIT_ALBUMARTIST, IDC_CHECK_ALBUMARTIST); break;
case IDC_CHECK_COMPOSER: HANDLE_CONTROL(IDC_EDIT_COMPOSER, IDC_CHECK_COMPOSER); break;
case IDC_CHECK_PUBLISHER: HANDLE_CONTROL(IDC_EDIT_PUBLISHER, IDC_CHECK_PUBLISHER); break;
case IDC_CHECK_TRACK: HANDLE_CONTROL(IDC_EDIT_TRACK, IDC_CHECK_TRACK); break;
case IDC_CHECK_DISC: HANDLE_CONTROL(IDC_EDIT_DISC, IDC_CHECK_DISC); break;
case IDC_CHECK_GENRE: HANDLE_CONTROL(IDC_EDIT_GENRE, IDC_CHECK_GENRE); break;
case IDC_CHECK_YEAR: HANDLE_CONTROL(IDC_EDIT_YEAR, IDC_CHECK_YEAR); break;
case IDC_CHECK_CATEGORY: HANDLE_CONTROL(IDC_EDIT_CATEGORY, IDC_CHECK_CATEGORY); break;
case IDC_CHECK_DIRECTOR: HANDLE_CONTROL(IDC_EDIT_DIRECTOR, IDC_CHECK_DIRECTOR); break;
case IDC_CHECK_PRODUCER: HANDLE_CONTROL(IDC_EDIT_PRODUCER, IDC_CHECK_PRODUCER); break;
case IDC_CHECK_PODCAST_CHANNEL: HANDLE_CONTROL(IDC_EDIT_PODCAST_CHANNEL, IDC_CHECK_PODCAST_CHANNEL); break;
case IDC_CHECK_BPM: HANDLE_CONTROL(IDC_EDIT_BPM, IDC_CHECK_BPM); break;
case IDC_CHECK_RATING: HANDLE_CONTROL(IDC_COMBO_RATING, IDC_CHECK_RATING); break;
case IDOK:
{
if (got_local_ml == 1)
{
wchar_t str[4] = {0};
StringCchPrintfW(str, 4, L"%d", !!IsDlgButtonChecked(hwndDlg, 1052));
WritePrivateProfileStringW(L"gen_ml_config", L"upd_tagz", str, ML_INI_FILE);
}
int ret = LPDialogBoxW(IDD_ADDSTUFF, hwndDlg, updateFiles_dialogProc);
if (!ret) break;
}
case IDCANCEL:
{
FreeQueryList();
GetWindowRect(hwndDlg, &editinfo_rect);
EndDialog(hwndDlg, 0);
break;
}
}
break;
case WM_LBUTTONDOWN:
{
POINTS p = MAKEPOINTS(lParam);
POINT p2 = {p.x, p.y};
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_ARTIST, IDC_CHECK_ARTIST)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_TITLE, IDC_CHECK_TITLE)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_ALBUM, IDC_CHECK_ALBUM)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_COMMENT, IDC_CHECK_COMMENT)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_ALBUMARTIST, IDC_CHECK_ALBUMARTIST)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_COMPOSER, IDC_CHECK_COMPOSER)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_PUBLISHER, IDC_CHECK_PUBLISHER)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_TRACK, IDC_CHECK_TRACK)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_GENRE, IDC_CHECK_GENRE)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_YEAR, IDC_CHECK_YEAR)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_DISC, IDC_CHECK_DISC)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_CATEGORY, IDC_CHECK_CATEGORY)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_DIRECTOR, IDC_CHECK_DIRECTOR)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_PRODUCER, IDC_CHECK_PRODUCER)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_PODCAST_CHANNEL, IDC_CHECK_PODCAST_CHANNEL)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_BPM, IDC_CHECK_BPM)) break;
if (checkEditInfoClick(hwndDlg, p2, IDC_COMBO_RATING, IDC_CHECK_RATING)) break;
}
break;
}
return FALSE;
}