diff options
Diffstat (limited to 'Src/Winamp/FileInfo.cpp')
-rw-r--r-- | Src/Winamp/FileInfo.cpp | 2534 |
1 files changed, 2534 insertions, 0 deletions
diff --git a/Src/Winamp/FileInfo.cpp b/Src/Winamp/FileInfo.cpp new file mode 100644 index 00000000..8c186586 --- /dev/null +++ b/Src/Winamp/FileInfo.cpp @@ -0,0 +1,2534 @@ +// 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)¶ms); + 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 = © + copyRecord(©, 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(©); + 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(©); + } + 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; +}
\ No newline at end of file |