Initial community commit

This commit is contained in:
Jef
2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit 20d28e80a5
16810 changed files with 4640254 additions and 2 deletions

View File

@ -0,0 +1,241 @@
#include "main.h"
#include "./amnwatcher.h"
#include <shlobj.h>
#include <strsafe.h>
#include "playlists.h"
#include "PlaylistManager.h"
#include "PlaylistView.h"
waServiceFactory *AMNWatcher::watcherFactory = NULL;
BOOL IsMusicNowWantUs(void)
{
HKEY key;
DWORD value = 0, length = sizeof(DWORD);
if (ERROR_SUCCESS == RegOpenKeyExW( HKEY_CURRENT_USER, L"Software\\AOL Music Now\\UserPreferences", 0, KEY_QUERY_VALUE, &key ))
{
long retCode = RegQueryValueExW(key, L"IntegrateWithWinamp", NULL, NULL, (LPBYTE) & value, &length);
if (ERROR_SUCCESS != retCode) value = TRUE;
RegCloseKey(key);
}
return value;
}
BOOL GetMusicNowPlaylistPath(LPWSTR *pathNew, LPWSTR *pathOld) // CAUTION!!! this function will allocate memory for the string using malloc
{
// first check rgistry
HKEY key;
*pathNew = NULL;
*pathOld = NULL;
if (ERROR_SUCCESS == RegOpenKeyExW( HKEY_CURRENT_USER, L"Software\\AOL Music Now\\UserPreferences", 0, KEY_QUERY_VALUE, &key ))
{
const wchar_t *rPath = L"PlaylistDirectory";
DWORD length;
if (ERROR_SUCCESS == RegQueryValueExW(key, rPath, NULL, NULL, NULL, &length)) // try registry ( for future)
{
*pathNew = (LPWSTR)malloc(length);
if (ERROR_SUCCESS != RegQueryValueExW(key, rPath, NULL, NULL, (LPBYTE)*pathNew, &length)) { free(*pathNew); *pathNew = NULL; }
}
RegCloseKey(key);
if (*pathNew && !PathFileExists(*pathNew)) { free(*pathNew); *pathNew = NULL; } //check that path is actualy exist
LPITEMIDLIST pidl;
if (S_OK == SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl))
{
wchar_t path[MAX_PATH*4] = {0};
if(SHGetPathFromIDListW(pidl, path))
{
const wchar_t *suffix = L"Music Now Playlists";
int cchLen = lstrlenW(path) + lstrlenW(suffix) + 32;
if (!*pathNew) // no luck - use deafult's
{
*pathNew = (LPWSTR)malloc(cchLen*sizeof(wchar_t));
StringCchPrintfW(*pathNew, cchLen, L"%s\\%s", path, suffix);
if (!PathFileExists(*pathNew)) {free(*pathNew); *pathNew = NULL; }// i give up - can't find anything
}
// now let's try old path
*pathOld = (LPWSTR)malloc(cchLen*sizeof(wchar_t));
StringCchPrintfW(*pathOld, cchLen, L"%s\\AOL %s", path, suffix);
if (!PathFileExists(*pathOld)) { free(*pathOld); *pathOld = NULL; }
}
CoTaskMemFree(pidl);
}
}
return (NULL != *pathNew) || (NULL != *pathOld);
}
AMNWatcher::AMNWatcher(void) : watcherOld(NULL), watcherNew(NULL), dirty(FALSE)
{}
AMNWatcher::~AMNWatcher(void)
{
//Destroy(); // have to just let it leak. you should call Destroy() manually
}
int AMNWatcher::Init(api_service *service, const wchar_t *trackPath)
{
BOOL retCode = FALSE;
if (!IsMusicNowWantUs()) return retCode;
if (watcherFactory) return retCode;
LPWSTR pathNew, pathOld;
if (!GetMusicNowPlaylistPath(&pathNew, &pathOld)) return retCode;
int cchLen = lstrlenW(pathNew);
if (cchLen > 1 && pathNew[cchLen -1] == L'\\') pathNew[cchLen -1] = 0x00;
cchLen = lstrlenW(pathOld);
if (cchLen > 1 && pathOld[cchLen -1] == L'\\') pathOld[cchLen -1] = 0x00;
watcherFactory = service->service_getServiceByGuid(watcherGUID);
if (watcherFactory)
{
if (pathNew)
{
watcherNew = (api_watcher*)watcherFactory->getInterface();
watcherNew->Create( L"WAMN_PLS_NEW", pathNew, TRUE, WATCHER_TRACKMODE_CENTRAL, OnWatcherNotify);
}
if (pathOld)
{
watcherOld = (api_watcher*)watcherFactory->getInterface();
watcherOld->Create( L"WAMN_PLS_OLD", pathOld, TRUE, WATCHER_TRACKMODE_CENTRAL, OnWatcherNotify);
}
for (int i = 0; i < 2; i++)
{
api_watcher *watcher = (i) ? watcherOld : watcherNew;
if (!watcher) continue;
watcher->SetExtensionFilter(L"WPL", WATCHER_FILTERTYPE_INCLUDE);
watcher->SetUserData(this);
watcher->SetCallBack(OnWatcherNotify);
watcher->SetTrackingPath(trackPath);
watcher->Start();
watcher->ForceScan(FALSE, SCANPRIORITY_IDLE);
}
}
free(pathNew);
free(pathOld);
return TRUE;
}
void AMNWatcher::Destroy(void)
{
if (watcherNew)
{
watcherNew->Stop();
watcherFactory->releaseInterface(watcherNew);
watcherNew = NULL;
}
if (watcherOld)
{
watcherOld->Stop();
watcherFactory->releaseInterface(watcherOld);
watcherOld = NULL;
}
}
int AMNWatcher::OnWatcherNotify(api_watcher *sender, UINT message, LONG_PTR param, void* userdata)
{
UNREFERENCED_PARAMETER(sender);
AMNWatcher *watcher = (AMNWatcher*)userdata;
if (WATCHER_MSG_FILE == message)
{
WATCHERCHANGEINFO *info = (WATCHERCHANGEINFO*)param;
// ignore some names
if (info->cchFile >= 21) // can be "AOL Music Now - Auido.wpl" or "AOL Music Now - Video.wpl"
{
const wchar_t *testname = info->file + info->cchFile - 21;
if (CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT, NULL, testname, 9, L"Music Now", 9)) return 1;
}
else if ( 17 == info->cchFile && CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT, NULL, info->file, 13, L"AOL Music Now", 13)) return 1;
wchar_t fullName[MAX_PATH*4];
PathCombineW(fullName, info->path, info->file);
size_t i(0);
switch (info->state)
{
case WATCHER_FILESTATE_ADDED:
case WATCHER_FILESTATE_CHANGED:
{
if (!info->file)
break;
wchar_t *title = _wcsdup(info->file);
unsigned int len = lstrlen(title);
while (len && title[len] != '.') len--;
if (len != 0) title[len] = 0x00;
while (i != playlists.size() && lstrcmpW(fullName, playlists.at(i).filename)) i++;
if ( i == playlists.size())
{
AddPlaylist(title, fullName, TRUE, playlistManager->CountItems(fullName));
watcher->dirty = TRUE;
}
else
{
int length = playlistManager->GetLengthMilliseconds(fullName);
int numItems = (int)playlistManager->CountItems(fullName);
if (playlists.at(i).length != length || playlists.at(i).numItems != numItems)
{
StringCchCopy(playlists.at(i).title, 1024, title);
playlists.at(i).length = length;
playlists.at(i).numItems = numItems;
HWND hwnd = (HWND) SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, 0, ML_IPC_GETCURRENTVIEW);
if (hwnd)
{
wchar_t* title = (wchar_t*)GetPropW(hwnd, L"TITLE");
if (title && 0 == lstrcmp(title, playlists[i].title)) SendMessage(hwnd, WM_PLAYLIST_RELOAD, 0, 0);
}
watcher->dirty = TRUE;
}
}
free(title);
}
break;
case WATCHER_FILESTATE_REMOVED:
{
for ( i = 0; i < playlists.size(); i++)
{
if (0 == lstrcmpW(fullName, playlists.at(i).filename))
{
HWND hwnd = (HWND) SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, 0, ML_IPC_GETCURRENTVIEW);
if (hwnd)
{
wchar_t* title = (wchar_t *)GetPropW(hwnd, L"TITLE");
if (title && 0 == lstrcmp(title, playlists[i].title)) SendMessage(hwnd, WM_PLAYLIST_UNLOAD, 0, 0);
}
if (playlists.at(i).treeId)
mediaLibrary.RemoveTreeItem(playlists.at(i).treeId);
playlists.eraseAt(i);
i--;
watcher->dirty = TRUE;
break;
}
}
}
break;
}
}
else if (WATCHER_MSG_STATUS == message)
{
if (STATUS_SCANER_STOPPED == param || STATUS_SCANER_FINISHED == param)
{
if (watcher->dirty)
{
SavePlaylists();
RefreshPlaylistsList();
watcher->dirty = FALSE;
}
}
}
return 1;
}

View File

@ -0,0 +1,124 @@
#include "main.h"
#include <shlwapi.h>
#include "resource.h"
wchar_t *createPlayListDBFileName(wchar_t *filename) // filename is ignored but used for temp space, make sure it's 1024+256 chars =)
{
wchar_t *filenameptr;
int x = 32;
for (;;)
{
GetTempFileNameW(g_path, L"plf", GetTickCount() + x*5000, filename);
if ( wcslen(filename) > 4)
{
if (g_config->ReadInt(L"playlist_m3u8", 1))
lstrcpyW(filename + wcslen(filename) - 4, L".m3u8");
else
lstrcpyW(filename + wcslen(filename) - 4, L".m3u");
}
HANDLE h = CreateFileW(filename, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, 0, 0);
if (h != INVALID_HANDLE_VALUE)
{
filenameptr = filename + wcslen(g_path) + 1;
CloseHandle(h);
break;
}
if (++x > 4096)
{
filenameptr = L"error.m3u";
break;
}
}
return filenameptr;
}
void playlists_AddToCloudPrompt(HWND hwndDlg)
{
if (g_config->ReadInt(L"cloud_always", 0) && !g_config->ReadInt(L"cloud_prompt", 0) && g_config->ReadInt(L"cloud", 1))
{
wchar_t titleStr[64] = {0};
if (MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_CLOUD_UNCHECKED),
WASABI_API_LNGSTRINGW_BUF(IDS_SENDTO_NEW_CLOUD_PLAYLIST, titleStr, 64),
MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDYES)
{
g_config->WriteInt(L"cloud", (IsDlgButtonChecked(hwndDlg, IDC_CLOUD) == BST_CHECKED));
}
else
{
CheckDlgButton(hwndDlg, IDC_CLOUD, playlists_CloudAvailable() && g_config->ReadInt(L"cloud", 1));
}
g_config->WriteInt(L"cloud_prompt", 1);
}
else
{
g_config->WriteInt(L"cloud", (IsDlgButtonChecked(hwndDlg, IDC_CLOUD) == BST_CHECKED));
}
}
int AddToCloud()
{
if (playlists_CloudAvailable())
{
if (g_config->ReadInt(L"cloud_always", 1))
{
g_config->WriteInt(L"cloud", 1);
return 1;
}
else
{
g_config->WriteInt(L"cloud", 0);
}
}
return 0;
}
INT_PTR CALLBACK AddPlaylistDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
SetProp(hwndDlg, L"pladdcb", (HANDLE)lParam);
CheckDlgButton(hwndDlg, IDC_CLOUD, AddToCloud());
PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_NAME), TRUE);
break;
}
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
{
wchar_t name[256] = {0};
GetDlgItemText(hwndDlg, IDC_NAME, name, 255);
if (!name[0])
{
wchar_t titleStr[32] = {0};
MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_ENTER_A_NAME),
WASABI_API_LNGSTRINGW_BUF(IDS_ERROR, titleStr, 32), MB_OK);
break;
}
wchar_t filename[1024 + 256] = {0};
createPlayListDBFileName(filename);
bool callback = !!GetProp(hwndDlg, L"pladdcb");
AddPlaylist(callback, name, filename, true, (playlists_CloudAvailable() ? g_config->ReadInt(L"cloud", 1) : 0));
if (callback) AGAVE_API_PLAYLISTS->Flush(); // REVIEW: save immediately? or only at the end?
EndDialog(hwndDlg, 1);
}
break;
case IDCANCEL:
EndDialog(hwndDlg, 0);
break;
case IDC_CLOUD:
{
playlists_AddToCloudPrompt(hwndDlg);
}
break;
}
break;
}
return FALSE;
};

View File

@ -0,0 +1,313 @@
#include <shlobj.h>
#include <shlwapi.h>
#include <strsafe.h>
#include "main.h"
#include "CurrentPlaylist.h"
#include "Playlist.h"
#include "../winamp/wa_ipc.h"
#include "../nu/AutoWide.h"
#include "PlaylistDirectoryCallback.h"
#include "api__ml_playlists.h"
extern Playlist currentPlaylist;
bool currentPlaylist_ImportFromDisk( HWND hwnd )
{
wchar_t oldCurPath[MAX_PATH] = {0};
GetCurrentDirectoryW(MAX_PATH, oldCurPath);
wchar_t temp[1024] = {0};
wchar_t filter[1024] = {0};
AGAVE_API_PLAYLISTMANAGER->GetFilterList(filter, 1024);
OPENFILENAMEW l = {sizeof(l), };
l.hwndOwner = hwnd;
l.lpstrFilter = filter;
l.lpstrFile = temp;
l.nMaxFile = 1023;
l.lpstrTitle = WASABI_API_LNGSTRINGW(IDS_IMPORT_PLAYLIST);
l.lpstrDefExt = L"m3u";
l.lpstrInitialDir = WASABI_API_APP->path_getWorkingPath();
l.Flags = OFN_HIDEREADONLY | OFN_EXPLORER;
bool ret = false;
if ( GetOpenFileNameW( &l ) )
{
wchar_t newCurPath[ MAX_PATH ] = { 0 };
GetCurrentDirectoryW( MAX_PATH, newCurPath );
WASABI_API_APP->path_setWorkingPath( newCurPath );
wchar_t titleStr[ 32 ] = { 0 };
int w = currentPlaylist.GetNumItems() == 0 ? IDYES :
MessageBox( hwnd, WASABI_API_LNGSTRINGW( IDS_APPEND_IMPORTED_PLAYLIST ),
WASABI_API_LNGSTRINGW_BUF( IDS_LIBRARY_QUESTION, titleStr, 32 ),
MB_YESNOCANCEL | MB_ICONQUESTION );
if ( w != IDCANCEL )
{
if ( w == IDNO )
currentPlaylist.Clear();
AGAVE_API_PLAYLISTMANAGER->Load( temp, &currentPlaylist );
ret = true;
}
}
SetCurrentDirectoryW( oldCurPath );
return ret;
}
bool currentPlaylist_ImportFromWinamp( HWND hwnd )
{
wchar_t titleStr[ 32 ] = { 0 };
int w = currentPlaylist.GetNumItems() == 0 ? IDNO :
MessageBox( hwnd, WASABI_API_LNGSTRINGW( IDS_APPEND_ACTIVE_PLAYLIST ),
WASABI_API_LNGSTRINGW_BUF( IDS_LIBRARY_QUESTION, titleStr, 32 ),
MB_YESNOCANCEL | MB_ICONQUESTION );
if ( w != IDCANCEL )
{
if ( w == IDNO )
currentPlaylist.Clear();
SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_WRITEPLAYLIST );
wchar_t *m3udir = (wchar_t *)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETM3UDIRECTORYW );
wchar_t s[ MAX_PATH ] = { 0 };
PathCombineW( s, m3udir, L"winamp.m3u8" );
AGAVE_API_PLAYLISTMANAGER->Load( s, &currentPlaylist );
return true;
}
return false;
}
bool CurrentPlaylist_DeleteMissing()
{
bool ret = false;
size_t x = currentPlaylist.GetNumItems();
while ( x-- )
{
wchar_t fn[ 1024 ] = { 0 };
currentPlaylist.GetItem( x, fn, 1024 );
if ( !wcsstr( fn, L"://" ) && !wcsstr( fn, L":\\\\" ) && !( PathFileExistsW( fn ) ) )
{
currentPlaylist.Remove( x );
ret = true;
}
}
return ret;
}
void CurrentPlaylist_Export(HWND dlgparent)
{
wchar_t oldCurPath[MAX_PATH] = {0};
GetCurrentDirectoryW(MAX_PATH, oldCurPath);
wchar_t temp[MAX_PATH] = {0};
OPENFILENAMEW l = {sizeof(OPENFILENAMEW), 0};
lstrcpynW(temp, (wchar_t*)GetPropW(dlgparent, L"TITLE"), MAX_PATH);
Playlists_ReplaceBadPathChars(temp);
l.hwndOwner = dlgparent;
l.hInstance = plugin.hDllInstance;
l.nFilterIndex = g_config->ReadInt(L"filter", 3);
l.lpstrFilter = (LPCWSTR)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 3, IPC_GET_PLAYLIST_EXTLISTW);
l.lpstrFile = temp;
l.nMaxFile = MAX_PATH;
l.lpstrTitle = WASABI_API_LNGSTRINGW(IDS_EXPORT_PLAYLIST);
l.lpstrDefExt = L"m3u";
l.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_OVERWRITEPROMPT;
if ( GetSaveFileNameW( &l ) )
{
wchar_t newCurPath[ MAX_PATH ] = { 0 };
GetCurrentDirectoryW( MAX_PATH, newCurPath );
WASABI_API_APP->path_setWorkingPath( newCurPath );
AGAVE_API_PLAYLISTMANAGER->Save( temp, &currentPlaylist );
}
g_config->WriteInt( L"filter", l.nFilterIndex );
SetCurrentDirectoryW( oldCurPath );
}
bool CurrentPlaylist_AddLocation( HWND hwndDlg )
{
bool ret = false;
char *p = (char *)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)hwndDlg, IPC_OPENURLBOX );
if ( p )
{
//size_t s = currentPlaylist.GetNumItems();
AutoWide wideFn( p );
if ( AGAVE_API_PLAYLISTMANAGER->Load( wideFn, &currentPlaylist ) != PLAYLISTMANAGER_SUCCESS )
{
wchar_t title[ FILETITLE_SIZE ] = { 0 };
int length = -1;
mediaLibrary.GetFileInfo( wideFn, title, FILETITLE_SIZE, &length );
currentPlaylist.AppendWithInfo( wideFn, title, length * 1000 );
}
ret = true;
// TODO: if (GetPrivateProfileInt("winamp", "rofiob", 1, WINAMP_INI)&1) PlayList_sort(2, s);
GlobalFree( (HGLOBAL)p );
}
return ret;
}
static INT_PTR CALLBACK browseCheckBoxProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
if ( uMsg == WM_INITDIALOG )
{
int rofiob = GetPrivateProfileIntA( "winamp", "rofiob", 1, mediaLibrary.GetWinampIni() );
if ( !( rofiob & 2 ) )
CheckDlgButton( hwndDlg, IDC_CHECK1, BST_CHECKED );
}
if ( uMsg == WM_COMMAND )
{
if ( LOWORD( wParam ) == IDC_CHECK1 )
{
int rofiob = GetPrivateProfileIntA( "winamp", "rofiob", 1, mediaLibrary.GetWinampIni() );
if ( IsDlgButtonChecked( hwndDlg, IDC_CHECK1 ) )
rofiob &= ~2;
else
rofiob |= 2;
char blah[ 32 ] = { 0 };
StringCchPrintfA( blah, 32, "%d", rofiob );
WritePrivateProfileStringA( "winamp", "rofiob", blah, mediaLibrary.GetWinampIni() );
}
}
return 0;
}
static int CALLBACK WINAPI BrowseCallbackProc( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData )
{
switch ( uMsg )
{
case BFFM_INITIALIZED:
{
SetWindowTextW( hwnd, WASABI_API_LNGSTRINGW( IDS_ADD_DIR_TO_PLAYLIST ) );
SendMessageW( hwnd, BFFM_SETSELECTIONW, 1, (LPARAM)WASABI_API_APP->path_getWorkingPath() );
HWND h2 = FindWindowEx( hwnd, NULL, NULL, L"__foo2" );
if ( h2 )
ShowWindow( h2, SW_HIDE );
HWND h = WASABI_API_CREATEDIALOGW( IDD_BROWSE_PLFLD, hwnd, browseCheckBoxProc );
SetWindowPos( h, 0, 4, 4, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
ShowWindow( h, SW_SHOWNA );
// this is not nice but it fixes the selection not working correctly on all OSes
EnumChildWindows( hwnd, browseEnumProc, 0 );
}
}
return 0;
}
bool CurrentPlaylist_AddDirectory( HWND hwndDlg )
{
BROWSEINFOW bi = { 0 };
wchar_t name[ MAX_PATH ] = { 0 };
bi.hwndOwner = hwndDlg;
bi.pidlRoot = 0;
bi.pszDisplayName = name;
bi.lpszTitle = L"__foo2";
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
bi.lpfn = BrowseCallbackProc;
bi.lParam = 0;
ITEMIDLIST *idlist = SHBrowseForFolderW( &bi );
if ( idlist )
{
//size_t s = currentPlaylist.GetNumItems();
wchar_t path[ MAX_PATH ] = { 0 };
SHGetPathFromIDListW( idlist, path );
WASABI_API_APP->path_setWorkingPath( path );
extern void Shell_Free( void *p );
Shell_Free( idlist );
WASABI_API_APP->path_setWorkingPath( path );
PlaylistDirectoryCallback dirCallback( mediaLibrary.GetExtensionList(), mediaLibrary.GetWinampIni() );
AGAVE_API_PLAYLISTMANAGER->LoadDirectory( path, &currentPlaylist, &dirCallback );
//int rofiob = GetPrivateProfileInt("winamp", "rofiob", 1, WINAMP_INI);
// TODO: if (rofiob&1) PlayList_sort(2, s);
return true;
}
return false;
}
bool CurrentPlaylist_AddFiles( HWND hwndDlg )
{
wchar_t oldCurPath[ MAX_PATH ] = { 0 };
GetCurrentDirectoryW( MAX_PATH, oldCurPath );
const int len = 256 * 1024 - 128;
wchar_t *temp;
OPENFILENAMEW l = { sizeof( l ), };
static int q;
if ( q )
return false;
q = 1;
temp = (wchar_t *)GlobalAlloc( GPTR, sizeof( wchar_t ) * len );
l.hwndOwner = hwndDlg;
wchar_t *fsb = (wchar_t *)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 1, IPC_GET_EXTLISTW );
l.lpstrFilter = fsb;
l.lpstrFile = temp;
l.nMaxFile = len - 1;
l.lpstrTitle = WASABI_API_LNGSTRINGW( IDS_ADD_FILES_TO_PLAYLIST );
l.lpstrDefExt = L"";
l.lpstrInitialDir = WASABI_API_APP->path_getWorkingPath();
l.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ALLOWMULTISELECT;
bool ret = false;
if ( GetOpenFileNameW( &l ) )
{
wchar_t newCurPath[ MAX_PATH ] = { 0 };
GetCurrentDirectoryW( MAX_PATH, newCurPath );
WASABI_API_APP->path_setWorkingPath( newCurPath );
if ( temp[ wcslen( temp ) + 1 ] )
{
AGAVE_API_PLAYLISTMANAGER->LoadFromDialog( temp, &currentPlaylist );
ret = true;
// TODO: if (GetPrivateProfileInt("winamp", "rofiob", 1, WINAMP_INI)&1) PlayList_sort(2, sp);
}
else
{
if ( AGAVE_API_PLAYLISTMANAGER->Load( temp, &currentPlaylist ) != PLAYLISTMANAGER_SUCCESS )
{
wchar_t title[ FILETITLE_SIZE ] = { 0 };
int length;
mediaLibrary.GetFileInfo( temp, title, FILETITLE_SIZE, &length );
currentPlaylist.AppendWithInfo( temp, title, length * 1000 );
ret = true;
}
}
}
SetCurrentDirectoryW( oldCurPath );
GlobalFree( fsb );
GlobalFree( temp );
q = 0;
return ret;
}

View File

@ -0,0 +1,12 @@
#ifndef NULLSOFT_ML_PLAYLISTS_CURRENTPLAYLIST_H
#define NULLSOFT_ML_PLAYLISTS_CURRENTPLAYLIST_H
bool currentPlaylist_ImportFromDisk(HWND hwnd);
bool currentPlaylist_ImportFromWinamp(HWND hwnd);
bool CurrentPlaylist_DeleteMissing();
void CurrentPlaylist_Export(HWND dlgparent);
bool CurrentPlaylist_AddLocation(HWND hwndDlg);
bool CurrentPlaylist_AddDirectory(HWND hwndDlg);
bool CurrentPlaylist_AddFiles(HWND hwndDlg);
#endif

View File

@ -0,0 +1,7 @@
since everything (hopefully) is being done on the main thread,
there isn't any locking done around the playlists vector.
We probably want to change that in the future
TODO:
ability to take a path from elsewhere

View File

@ -0,0 +1,331 @@
#include "main.h"
#include "./playlist.h"
#include <algorithm>
#include "nu/MediaLibraryInterface.h"
#include "Winamp/strutil.h"
Playlist::~Playlist()
{
Clear();
}
void Playlist::Clear()
{
for ( pl_entry *entry : entries )
delete entry;
entries.clear();
lengthInMS = 0;
}
void Playlist::OnFile( const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info )
{
entries.push_back( new pl_entry( filename, title, lengthInMS, info ) );
this->lengthInMS += lengthInMS;
}
void Playlist::AppendWithInfo( const wchar_t *filename, const wchar_t *title, int lengthInMS )
{
this->lengthInMS += lengthInMS;
entries.push_back( new pl_entry( filename, title, lengthInMS ) );
}
void Playlist::AppendWithInfo( const wchar_t *filename, const wchar_t *title, int lengthInMS, std::map<std::wstring, std::wstring> &p_extended_infos )
{
this->lengthInMS += lengthInMS;
pl_entry *l_new_pl_entry = new pl_entry( filename, title, lengthInMS );
if ( !p_extended_infos.empty() )
{
for ( auto l_extended_info : p_extended_infos )
l_new_pl_entry->_extended_infos.emplace( _wcsdup( l_extended_info.first.c_str() ), _wcsdup( l_extended_info.second.c_str() ) );
}
entries.push_back( l_new_pl_entry );
}
size_t Playlist::GetNumItems()
{
return entries.size();
}
size_t Playlist::GetItem( size_t item, wchar_t *filename, size_t filenameCch )
{
if ( item >= entries.size() )
return 0;
return entries[ item ]->GetFilename( filename, filenameCch );
}
size_t Playlist::GetItemTitle( size_t item, wchar_t *title, size_t titleCch )
{
if ( item >= entries.size() )
return 0;
return entries[ item ]->GetTitle( title, titleCch );
}
const wchar_t *Playlist::ItemTitle( size_t item )
{
if ( item >= entries.size() )
return 0;
return entries[ item ]->filetitle;
}
const wchar_t *Playlist::ItemName( size_t item )
{
if ( item >= entries.size() )
return 0;
return entries[ item ]->filename;
}
int Playlist::GetItemLengthMilliseconds( size_t item )
{
if ( item >= entries.size() )
return -1;
return entries[ item ]->GetLengthInMilliseconds();
}
size_t Playlist::GetItemExtendedInfo( size_t item, const wchar_t *metadata, wchar_t *info, size_t infoCch )
{
if ( item >= entries.size() )
return 0;
return entries[ item ]->GetExtendedInfo( metadata, info, infoCch );
}
int Playlist::Reverse()
{
// TODO: keep a bool flag and just do size-item-1 every time a GetItem* function is called
std::reverse( entries.begin(), entries.end() );
return PLAYLIST_SUCCESS;
}
int Playlist::Swap( size_t item1, size_t item2 )
{
std::swap( entries[ item1 ], entries[ item2 ] );
return PLAYLIST_SUCCESS;
}
class RandMod
{
public:
RandMod( int ( *_generator )( ) ) : generator( _generator ) {}
int operator ()( int n ) { return generator() % n; }
int ( *generator )( );
};
int Playlist::Randomize( int ( *generator )( ) )
{
RandMod randMod( generator );
std::random_shuffle( entries.begin(), entries.end(), randMod );
return PLAYLIST_SUCCESS;
}
void Playlist::Remove( size_t item )
{
lengthInMS -= entries[item]->length;
delete entries[item];
entries.erase(entries.begin() + item);
}
void Playlist::SetItemFilename( size_t item, const wchar_t *filename )
{
if ( item < entries.size() )
entries[ item ]->SetFilename( filename );
}
void Playlist::SetItemTitle( size_t item, const wchar_t *title )
{
if ( item < entries.size() )
entries[ item ]->SetTitle( title );
}
void Playlist::SetItemLengthMilliseconds( size_t item, int length )
{
if ( item < entries.size() )
{
lengthInMS -= entries[ item ]->length;
entries[ item ]->SetLengthMilliseconds( length );
lengthInMS += length;
}
}
void GetTitle( pl_entry *&a )
{
if ( !a->cached )
{
wchar_t title[ FILETITLE_SIZE ] = { 0 };
int length = -1;
mediaLibrary.GetFileInfo( a->filename, title, FILETITLE_SIZE, &length );
a->SetLengthMilliseconds( length * 1000 );
a->SetTitle( title );
}
}
static bool PlayList_sortByTitle( pl_entry *&a, pl_entry *&b )
{
GetTitle( a );
GetTitle( b );
int comp = CompareStringW( LOCALE_USER_DEFAULT, NORM_IGNORECASE /*|NORM_IGNOREKANATYPE*/ | NORM_IGNOREWIDTH, a->filetitle, -1, b->filetitle, -1 );
return comp == CSTR_LESS_THAN;
// TODO: grab this function from winamp - return CompareStringLogical(a.strTitle, b.strTitle)<0;
}
static bool PlayList_sortByFile( pl_entry *&a, pl_entry *&b ) //const void *a, const void *b)
{
const wchar_t *file1 = PathFindFileNameW( a->filename );
const wchar_t *file2 = PathFindFileNameW( b->filename );
int comp = CompareStringW( LOCALE_USER_DEFAULT, NORM_IGNORECASE | /*NORM_IGNOREKANATYPE |*/ NORM_IGNOREWIDTH, file1, -1, file2, -1 );
return comp == CSTR_LESS_THAN;
// TODO: grab this function from winamp - return FileCompareLogical(file1, file2)<0;
}
static bool PlayList_sortByDirectory( pl_entry *&a, pl_entry *&b ) // by dir, then by title
{
const wchar_t *directory1 = a->filename;
const wchar_t *directory2 = b->filename;
const wchar_t *directoryEnd1 = scanstr_backcW( directory1, L"\\", 0 );
const wchar_t *directoryEnd2 = scanstr_backcW( directory2, L"\\", 0 );
size_t dirLen1 = directoryEnd1 - directory1;
size_t dirLen2 = directoryEnd2 - directory2;
if ( !dirLen1 && !dirLen2 ) // both in the current directory?
return PlayList_sortByFile( a, b ); // not optimized, because the function does another scanstr_back, but easy for now :)
if ( !dirLen1 ) // only the first dir is empty?
return true; // sort it first
if ( !dirLen2 ) // only the second dir empty?
return false; // empty dirs go first
#if 0 // TODO: grab this function from winamp
int comp = FileCompareLogicalN( directory1, dirLen1, directory2, dirLen2 );
if ( comp == 0 )
return PlayList_sortByFile( a, b );
else
return comp < 0;
#endif
int comp = CompareStringW( LOCALE_USER_DEFAULT, NORM_IGNORECASE | /*NORM_IGNOREKANATYPE | */NORM_IGNOREWIDTH, directory1, dirLen1, directory2, dirLen2 );
if ( comp == CSTR_EQUAL ) // same dir
return PlayList_sortByFile( a, b ); // do second sort
else // different dirs
return comp == CSTR_LESS_THAN;
}
int Playlist::SortByTitle()
{
std::sort( entries.begin(), entries.end(), PlayList_sortByTitle );
return 1;
}
int Playlist::SortByFilename()
{
std::sort( entries.begin(), entries.end(), PlayList_sortByFile );
return 1;
}
int Playlist::SortByDirectory()
{
std::sort( entries.begin(), entries.end(), PlayList_sortByDirectory );
return 1;
}
/*
int Playlist::Move(size_t itemSrc, size_t itemDest)
{
if (itemSrc < itemDest)
std::rotate(&entries[itemSrc], &entries[itemSrc], &entries[itemDest]);
else
if (itemSrc > itemDest)
std::rotate(&entries[itemDest], &entries[itemSrc], &entries[itemSrc]);
return 1;
}*/
bool Playlist::IsCached( size_t item )
{
return entries[ item ]->cached;
}
bool Playlist::IsLocal( size_t item )
{
return entries[ item ]->isLocal();
}
void Playlist::ClearCache( size_t item )
{
entries[ item ]->cached = false;
}
void Playlist::InsertPlaylist( Playlist &copy, size_t index )
{
for ( pl_entry *l_entry : copy.entries )
{
entries.insert( entries.begin() + index, l_entry );
++index;
lengthInMS += l_entry->length;
}
copy.entries.clear();
}
void Playlist::AppendPlaylist( Playlist &copy )
{
for ( pl_entry *l_entry : copy.entries )
{
this->entries.push_back( l_entry );
lengthInMS += l_entry->length;
}
copy.entries.clear();
}
#ifdef CBCLASS
#undef CBCLASS
#endif
#define CBCLASS Playlist
START_MULTIPATCH;
START_PATCH( patch_playlist )
M_VCB( patch_playlist, ifc_playlist, IFC_PLAYLIST_CLEAR, Clear )
//M_VCB(patch_playlist, ifc_playlist, IFC_PLAYLIST_APPENDWITHINFO, AppendWithInfo)
//M_VCB(patch_playlist, ifc_playlist, IFC_PLAYLIST_APPEND, Append)
M_CB( patch_playlist, ifc_playlist, IFC_PLAYLIST_GETNUMITEMS, GetNumItems )
M_CB( patch_playlist, ifc_playlist, IFC_PLAYLIST_GETITEM, GetItem )
M_CB( patch_playlist, ifc_playlist, IFC_PLAYLIST_GETITEMTITLE, GetItemTitle )
M_CB( patch_playlist, ifc_playlist, IFC_PLAYLIST_GETITEMLENGTHMILLISECONDS, GetItemLengthMilliseconds )
M_CB( patch_playlist, ifc_playlist, IFC_PLAYLIST_GETITEMEXTENDEDINFO, GetItemExtendedInfo )
M_CB( patch_playlist, ifc_playlist, IFC_PLAYLIST_REVERSE, Reverse )
M_CB( patch_playlist, ifc_playlist, IFC_PLAYLIST_SWAP, Swap )
M_CB( patch_playlist, ifc_playlist, IFC_PLAYLIST_RANDOMIZE, Randomize )
M_VCB( patch_playlist, ifc_playlist, IFC_PLAYLIST_REMOVE, Remove )
M_CB( patch_playlist, ifc_playlist, IFC_PLAYLIST_SORTBYTITLE, SortByTitle )
M_CB( patch_playlist, ifc_playlist, IFC_PLAYLIST_SORTBYFILENAME, SortByFilename )
M_CB( patch_playlist, ifc_playlist, IFC_PLAYLIST_SORTBYDIRECTORY, SortByDirectory )
NEXT_PATCH( patch_playlistloadercallback )
M_VCB( patch_playlistloadercallback, ifc_playlistloadercallback, IFC_PLAYLISTLOADERCALLBACK_ONFILE, OnFile );
END_PATCH
END_MULTIPATCH;

View File

@ -0,0 +1,66 @@
#ifndef NULLSOFT_ML_PLAYLISTS_PLAYLIST_H
#define NULLSOFT_ML_PLAYLISTS_PLAYLIST_H
#include "playlist/ifc_playlist.h"
#include "bfc/multipatch.h"
#include "playlist/pl_entry.h"
#include "playlist/ifc_playlistloadercallback.h"
enum
{
patch_playlist,
patch_playlistloadercallback
};
class Playlist : public MultiPatch<patch_playlist, ifc_playlist>, public MultiPatch<patch_playlistloadercallback, ifc_playlistloadercallback>
{
public:
Playlist() {}
virtual ~Playlist();
void Clear();
void OnFile( const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info );
void AppendWithInfo( const wchar_t *filename, const wchar_t *title, int lengthInMS );
void AppendWithInfo( const wchar_t *filename, const wchar_t *title, int lengthInMS, std::map<std::wstring, std::wstring> &p_extended_infos );
size_t GetNumItems();
size_t GetItem( size_t item, wchar_t *filename, size_t filenameCch );
size_t GetItemTitle( size_t item, wchar_t *title, size_t titleCch );
const wchar_t *ItemTitle( size_t item );
const wchar_t *ItemName( size_t item );
int GetItemLengthMilliseconds( size_t item ); // TODO: maybe microsecond for better resolution?
size_t GetItemExtendedInfo( size_t item, const wchar_t *metadata, wchar_t *info, size_t infoCch );
bool IsCached( size_t item );
bool IsLocal( size_t item );
void ClearCache( size_t item );
void SetItemFilename( size_t item, const wchar_t *filename );
void SetItemTitle( size_t item, const wchar_t *title );
void SetItemLengthMilliseconds( size_t item, int length );
int Reverse();
int Swap( size_t item1, size_t item2 );
int Randomize( int ( *generator )( ) );
void Remove( size_t item );
int SortByTitle();
int SortByFilename();
int SortByDirectory(); //sorts by directory and then by filename
void InsertPlaylist( Playlist &copy, size_t index );
void AppendPlaylist( Playlist &copy );
protected:
RECVS_MULTIPATCH;
public:
typedef std::vector<pl_entry*> PlaylistEntries;
PlaylistEntries entries;
uint64_t lengthInMS = 0;
};
#endif // !NULLSOFT_ML_PLAYLISTS_PLAYLIST_H

View File

@ -0,0 +1,41 @@
#include "main.h"
#include "PlaylistDirectoryCallback.h"
PlaylistDirectoryCallback::PlaylistDirectoryCallback(const char *_extlist, const char *winampIni)
: extlist(_extlist), recurse(true)
{
if (winampIni)
{
int rofiob = GetPrivateProfileIntA("winamp", "rofiob", 1, winampIni);
recurse = (rofiob & 2) ? false : true;
}
}
bool PlaylistDirectoryCallback::ShouldRecurse(const wchar_t *path)
{
return recurse;
}
bool PlaylistDirectoryCallback::ShouldLoad(const wchar_t *filename)
{
const wchar_t *ext = PathFindExtensionW(filename);
if (!*ext)
return false;
ext++;
const char *a = extlist;
while (a && *a)
{
if (!lstrcmpiW(AutoWide(a), ext))
return true;
a += lstrlenA(a) + 1;
}
return false;
}
#define CBCLASS PlaylistDirectoryCallback
START_DISPATCH;
CB( IFC_PLAYLISTDIRECTORYCALLBACK_SHOULDRECURSE, ShouldRecurse )
CB( IFC_PLAYLISTDIRECTORYCALLBACK_SHOULDLOAD, ShouldLoad )
END_DISPATCH;

View File

@ -0,0 +1,15 @@
#include "../playlist/ifc_playlistdirectorycallback.h"
class PlaylistDirectoryCallback : public ifc_playlistdirectorycallback
{
public:
PlaylistDirectoryCallback(const char *_extlist, const char *winampIni=0);
bool ShouldRecurse(const wchar_t *path);
bool ShouldLoad(const wchar_t *filename);
const char *extlist;
bool recurse;
protected:
RECVS_DISPATCH;
};

View File

@ -0,0 +1,233 @@
#include <assert.h>
#include <strsafe.h>
#include "main.h"
#include "PlaylistInfo.h"
#include "nu/AutoLock.h"
int uniqueAddress;
using namespace Nullsoft::Utility;
static INT_PTR FindTreeID( GUID playlist_guid )
{
INT_PTR treeId = 0;
//if ( tree_to_guid_map.reverseLookup( playlist_guid, &treeId ) )
// return treeId;
//else
// return 0;
// Look for values and return the index
for (auto& item : tree_to_guid_map)
{
if (0 == memcmp(&item.second, &playlist_guid, sizeof(GUID)))
{
return item.first;
}
}
return 0;
}
PlaylistInfo::PlaylistInfo()
{
Clear();
}
void PlaylistInfo::Clear()
{
length = 0;
numItems = 0;
treeId = 0;
index = 0;
iterator = 0;
cloud = 0;
playlist_guid = INVALID_GUID;
}
bool PlaylistInfo::Associate( INT_PTR _treeId )
{
Clear();
treeId = _treeId;
playlist_guid = tree_to_guid_map[treeId];
if ( AGAVE_API_PLAYLISTS->GetPosition( playlist_guid, &index ) == API_PLAYLISTS_SUCCESS )
{
iterator = AGAVE_API_PLAYLISTS->GetIterator();
AGAVE_API_PLAYLISTS->GetInfo( index, api_playlists_itemCount, &numItems, sizeof( numItems ) );
AGAVE_API_PLAYLISTS->GetInfo( index, api_playlists_totalTime, &length, sizeof( length ) );
AGAVE_API_PLAYLISTS->GetInfo( index, api_playlists_cloud, &cloud, sizeof( cloud ) );
}
else
playlist_guid = INVALID_GUID;
return Valid();
}
// as a pre-condition, AGAVE_API_PLAYLISTS needs to be locked
PlaylistInfo::PlaylistInfo( size_t p_index )
{
Clear();
index = p_index;
iterator = AGAVE_API_PLAYLISTS->GetIterator();
playlist_guid = AGAVE_API_PLAYLISTS->GetGUID( index );
AGAVE_API_PLAYLISTS->GetInfo( index, api_playlists_itemCount, &numItems, sizeof( numItems ) );
AGAVE_API_PLAYLISTS->GetInfo( index, api_playlists_totalTime, &length, sizeof( length ) );
AGAVE_API_PLAYLISTS->GetInfo( index, api_playlists_cloud, &cloud, sizeof( cloud ) );
treeId = FindTreeID( playlist_guid ); // try to find treeId
}
// as a pre-condition, AGAVE_API_PLAYLISTS needs to be locked
PlaylistInfo::PlaylistInfo( GUID p_playlist_guid )
{
Clear();
playlist_guid = p_playlist_guid;
if ( AGAVE_API_PLAYLISTS->GetPosition( playlist_guid, &index ) == API_PLAYLISTS_SUCCESS )
{
iterator = AGAVE_API_PLAYLISTS->GetIterator();
AGAVE_API_PLAYLISTS->GetInfo( index, api_playlists_itemCount, &numItems, sizeof( numItems ) );
AGAVE_API_PLAYLISTS->GetInfo( index, api_playlists_totalTime, &length, sizeof( length ) );
AGAVE_API_PLAYLISTS->GetInfo( index, api_playlists_cloud, &cloud, sizeof( cloud ) );
treeId = FindTreeID( playlist_guid ); // try to find treeId
}
else
playlist_guid = INVALID_GUID;
}
PlaylistInfo::PlaylistInfo( const PlaylistInfo &copy )
{
Clear();
index = copy.index;
iterator = copy.iterator;
playlist_guid = copy.playlist_guid;
length = copy.length;
numItems = copy.numItems;
treeId = copy.treeId;
cloud = copy.cloud;
}
size_t PlaylistInfo::GetIndex()
{
assert( Valid() );
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
size_t curIterator = AGAVE_API_PLAYLISTS->GetIterator();
if ( curIterator != iterator )
{
iterator = curIterator;
AGAVE_API_PLAYLISTS->GetPosition( playlist_guid, &index );
}
return index;
}
void PlaylistInfo::IssueSaveCallback()
{
assert( Valid() );
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_SAVED, GetIndex(), (intptr_t)&uniqueAddress );
}
void PlaylistInfo::Refresh()
{
assert( Valid() );
if ( AGAVE_API_PLAYLISTS->GetPosition( playlist_guid, &index ) == API_PLAYLISTS_SUCCESS )
{
iterator = AGAVE_API_PLAYLISTS->GetIterator();
AGAVE_API_PLAYLISTS->GetInfo( index, api_playlists_itemCount, &numItems, sizeof( numItems ) );
AGAVE_API_PLAYLISTS->GetInfo( index, api_playlists_totalTime, &length, sizeof( length ) );
}
else
playlist_guid = INVALID_GUID;
}
const wchar_t *PlaylistInfo::GetFilename()
{
assert( Valid() );
if ( _filename.empty() )
_filename = AGAVE_API_PLAYLISTS->GetFilename( GetIndex() );
return _filename.c_str();
}
// TODO: we should investigate caching the title
const wchar_t *PlaylistInfo::GetName()
{
assert( Valid() );
if ( _title.empty() )
_title = AGAVE_API_PLAYLISTS->GetName( GetIndex() );
return _title.c_str();
}
size_t PlaylistInfo::GetLength()
{
assert( Valid() );
return length;
}
void PlaylistInfo::SetLength( size_t newLength )
{
assert( Valid() );
length = newLength;
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
AGAVE_API_PLAYLISTS->SetInfo( GetIndex(), api_playlists_totalTime, &length, sizeof( length ) );
}
size_t PlaylistInfo::GetSize()
{
assert( Valid() );
return numItems;
}
size_t PlaylistInfo::GetCloud()
{
assert( Valid() );
return cloud;
}
void PlaylistInfo::SetCloud(size_t newCloud)
{
assert( Valid() );
cloud = newCloud;
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
AGAVE_API_PLAYLISTS->SetInfo( GetIndex(), api_playlists_cloud, &cloud, sizeof( cloud ) );
}
bool PlaylistInfo::Valid()
{
return !!( playlist_guid != INVALID_GUID );
}
void PlaylistInfo::SetSize( size_t newSize )
{
assert( Valid() );
numItems = newSize;
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
AGAVE_API_PLAYLISTS->SetInfo( GetIndex(), api_playlists_itemCount, &numItems, sizeof( numItems ) );
}

View File

@ -0,0 +1,58 @@
#ifndef NULLSOFT_ML_PLAYLISTS_PLAYLIST_INFO_H
#define NULLSOFT_ML_PLAYLISTS_PLAYLIST_INFO_H
#include <windows.h> // for MAX_PATH
#include <iostream> // for std::wstring
// REVIEW: what if we want this to be an ifc_playlist * from elsewhere instead of a physical m3u file
// maybe this should be a playlist factory instead?
class PlaylistInfo
{
/* --- Methods --- */
public:
PlaylistInfo();
PlaylistInfo( GUID _playlist_guid );
PlaylistInfo( size_t p_index ); // as a pre-condition, AGAVE_API_PLAYLISTS needs to be locked
PlaylistInfo( const PlaylistInfo &copy );
bool Valid();
bool Associate( INT_PTR _treeId );
size_t GetIndex();
const wchar_t *GetFilename();
const wchar_t *GetName();
size_t GetLength();
void SetLength( size_t newLength );
size_t GetSize();
void SetSize( size_t newSize );
size_t GetCloud();
void SetCloud( size_t newCloud );
void Refresh();
void IssueSaveCallback();
private:
void Clear();
/* --- Data --- */
public:
INT_PTR treeId; // if it's being displayed as a media library tree, the id is here (0 otherwise)
GUID playlist_guid;
private:
size_t index;
size_t iterator;
size_t length; // in seconds
size_t numItems;
size_t cloud;
std::wstring _title;
std::wstring _filename;
};
#endif

View File

@ -0,0 +1,300 @@
#include "main.h"
#include "PlaylistView.h"
#include "Playlist.h"
#include "CurrentPlaylist.h"
#include "api__ml_playlists.h"
#include "../ml_local/api_mldb.h"
#include "../ml_pmp/pmp.h"
#include <strsafe.h>
extern Playlist currentPlaylist;
static BOOL playlist_GetDisplayInfo( NMLVDISPINFO *lpdi )
{
size_t item = lpdi->item.iItem;
if ( item < 0 || item >= currentPlaylist.GetNumItems() )
return 0;
if ( lpdi->item.mask & LVIF_TEXT )
{
switch ( lpdi->item.iSubItem )
{
case 0:
{
if ( !currentPlaylist.IsCached( item ) )
{
wchar_t title[ FILETITLE_SIZE ] = { 0 };
int length = -1;
mediaLibrary.GetFileInfo( currentPlaylist.ItemName( item ), title, FILETITLE_SIZE, &length );
currentPlaylist.SetItemLengthMilliseconds( item, length * 1000 );
currentPlaylist.SetItemTitle( item, title );
}
// CUT: currentPlaylist.GetItemTitle(item, lpdi->item.pszText, lpdi->item.cchTextMax);
const wchar_t *title = currentPlaylist.ItemTitle( item );
if ( !title )
title = currentPlaylist.ItemName( item );
// TODO - just using for debugging to check values
#ifdef DEBUG
wchar_t info[ 128 ] = { 0 };
if ( currentPlaylist.GetItemExtendedInfo( item, L"cloud", info, 128 ) )
{
StringCchPrintf( lpdi->item.pszText, lpdi->item.cchTextMax, L"[%s] %d. %s", info, item + 1, title );
}
else
{
#endif
StringCchPrintf( lpdi->item.pszText, lpdi->item.cchTextMax, L"%d. %s", item + 1, title );
#ifdef DEBUG
}
#endif
}
break;
case 1:
{
wchar_t info[ 16 ] = { 0 };
if ( currentPlaylist.GetItemExtendedInfo( item, L"cloud_status", info, 16 ) )
{
StringCchPrintf( lpdi->item.pszText, lpdi->item.cchTextMax, L"%s", info );
}
else
StringCchPrintf( lpdi->item.pszText, lpdi->item.cchTextMax, L"%d", 4 );
}
break;
case 2:
{
if ( currentPlaylist.GetItemLengthMilliseconds( item ) == 0 ) // if the length is 0, then we'll re-read it
{
wchar_t title[ FILETITLE_SIZE ] = { 0 };
int length = 0;
mediaLibrary.GetFileInfo( currentPlaylist.ItemName( item ), title, FILETITLE_SIZE, &length );
if ( length == 0 )
currentPlaylist.SetItemLengthMilliseconds( item, -1000 );
else
{
currentPlaylist.SetItemLengthMilliseconds( item, length * 1000 );
}
}
int length = currentPlaylist.GetItemLengthMilliseconds( item ) / 1000;
if ( length <= 0 )
lpdi->item.pszText[ 0 ] = 0;
else
StringCchPrintf( lpdi->item.pszText, lpdi->item.cchTextMax, L"%d:%02d", length / 60, length % 60 );
}
break;
}
}
return 0;
}
BOOL playlist_OnCustomDraw( HWND hwndDlg, NMLVCUSTOMDRAW *plvcd, LRESULT *pResult )
{
static BOOL bDrawFocus;
static RECT rcView;
static CLOUDCOLUMNPAINT cloudColumnPaint;
*pResult = CDRF_DODEFAULT;
switch ( plvcd->nmcd.dwDrawStage )
{
case CDDS_PREPAINT:
*pResult |= CDRF_NOTIFYITEMDRAW;
CopyRect( &rcView, &plvcd->nmcd.rc );
cloudColumnPaint.hwndList = plvcd->nmcd.hdr.hwndFrom;
cloudColumnPaint.hdc = plvcd->nmcd.hdc;
cloudColumnPaint.prcView = &rcView;
return TRUE;
case CDDS_ITEMPREPAINT:
*pResult |= CDRF_NOTIFYSUBITEMDRAW;
bDrawFocus = ( CDIS_FOCUS & plvcd->nmcd.uItemState );
if ( bDrawFocus )
{
plvcd->nmcd.uItemState &= ~CDIS_FOCUS;
*pResult |= CDRF_NOTIFYPOSTPAINT;
}
return TRUE;
case CDDS_ITEMPOSTPAINT:
if ( bDrawFocus )
{
RECT rc;
rc.left = LVIR_BOUNDS;
SendMessageW( plvcd->nmcd.hdr.hwndFrom, LVM_GETITEMRECT, plvcd->nmcd.dwItemSpec, (LPARAM)&rc );
rc.left += 3;
DrawFocusRect( plvcd->nmcd.hdc, &rc );
plvcd->nmcd.uItemState |= CDIS_FOCUS;
bDrawFocus = FALSE;
}
*pResult = CDRF_SKIPDEFAULT;
return TRUE;
case( CDDS_SUBITEM | CDDS_ITEMPREPAINT ):
// TODO need to have a map between column ids so we do this correctly
if ( plvcd->iSubItem == 1 )
{
if ( 0 == plvcd->iSubItem && 0 == plvcd->nmcd.rc.right )
break;
cloudColumnPaint.iItem = plvcd->nmcd.dwItemSpec;
cloudColumnPaint.iSubItem = plvcd->iSubItem;
int cloud_icon = 4;
size_t item = plvcd->nmcd.dwItemSpec;
wchar_t info[ 16 ] = { 0 };
if ( currentPlaylist.GetItemExtendedInfo( item, L"cloud_status", info, 16 ) )
cloud_icon = _wtoi( info );
// TODO have this show an appropriate cloud icon for the playlist
// currently all we have is cloud or nothing as we'll only
// have files locally for this for the moment (need todo!!!)
cloudColumnPaint.value = cloud_icon;
cloudColumnPaint.prcItem = &plvcd->nmcd.rc;
cloudColumnPaint.rgbBk = plvcd->clrTextBk;
cloudColumnPaint.rgbFg = plvcd->clrText;
if ( MLCloudColumn_Paint( plugin.hwndLibraryParent, &cloudColumnPaint ) )
{
*pResult = CDRF_SKIPDEFAULT;
return TRUE;
}
}
break;
}
return FALSE;
}
BOOL playlist_Notify( HWND hwndDlg, WPARAM wParam, LPARAM lParam )
{
LPNMHDR l = (LPNMHDR)lParam;
if ( l->idFrom == IDC_PLAYLIST_EDITOR )
{
switch ( l->code )
{
case NM_DBLCLK:
PlaySelection( g_config->ReadInt( L"enqueuedef", 0 ) == 1, g_config->ReadInt( L"plplaymode", 1 ) );
break;
case LVN_GETDISPINFO:
return playlist_GetDisplayInfo( (NMLVDISPINFO *)lParam );
case LVN_BEGINDRAG:
we_are_drag_and_dropping = 1;
SetCapture( hwndDlg );
break;
case LVN_ITEMCHANGED:
case LVN_ODSTATECHANGED:
UpdatePlaylistTime( hwndDlg );
break;
case NM_CUSTOMDRAW:
{
LRESULT result = 0;
if ( cloud_avail && playlist_OnCustomDraw( hwndDlg, (NMLVCUSTOMDRAW *)lParam, &result ) )
{
SetWindowLongPtrW( hwndDlg, DWLP_MSGRESULT, (LONG_PTR)result );
return 1;
}
break;
}
case NM_CLICK:
{
LPNMITEMACTIVATE pnmitem = (LPNMITEMACTIVATE)lParam;
if ( cloud_avail && pnmitem->iItem != -1 && pnmitem->iSubItem == 1 )
{
RECT itemRect = { 0 };
if ( pnmitem->iSubItem )
ListView_GetSubItemRect( pnmitem->hdr.hwndFrom, pnmitem->iItem, pnmitem->iSubItem, LVIR_BOUNDS, &itemRect );
else
{
ListView_GetItemRect( pnmitem->hdr.hwndFrom, pnmitem->iItem, &itemRect, LVIR_BOUNDS );
itemRect.right = itemRect.left + ListView_GetColumnWidth( pnmitem->hdr.hwndFrom, pnmitem->iSubItem );
}
MapWindowPoints( l->hwndFrom, HWND_DESKTOP, (POINT *)&itemRect, 2 );
//int cloud_devices = 0;
HMENU cloud_hmenu = 0;
int mark = playlist_list.GetSelectionMark();
if ( mark != -1 )
{
wchar_t filename[ MAX_PATH ] = { 0 };
currentPlaylist.entries[ mark ]->GetFilename( filename, MAX_PATH );
cloud_hmenu = CreatePopupMenu();
WASABI_API_SYSCB->syscb_issueCallback( api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_GET_CLOUD_STATUS, (intptr_t)&filename, (intptr_t)&cloud_hmenu );
if ( cloud_hmenu )
{
int r = Menu_TrackPopup( plugin.hwndLibraryParent, cloud_hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_NONOTIFY, itemRect.right, itemRect.top, hwndDlg, NULL );
if ( r >= CLOUD_SOURCE_MENUS && r < CLOUD_SOURCE_MENUS_UPPER )
{ // deals with cloud specific menus
// 0 = no change
// 1 = adding to cloud
// 2 = added locally
// 4 = removed
int mode = 0; // deals with cloud specific menus
WASABI_API_SYSCB->syscb_issueCallback( api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_PROCESS_CLOUD_STATUS, (intptr_t)r, (intptr_t)&mode );
// TODO
/*switch (mode)
{
case 1:
setCloudValue(&itemCache.Items[pnmitem->iItem], L"5");
break;
case 2:
setCloudValue(&itemCache.Items[pnmitem->iItem], L"4");
break;
case 4:
setCloudValue(&itemCache.Items[pnmitem->iItem], L"4");
break;
}
InvalidateRect(resultlist.getwnd(), NULL, TRUE);*/
}
DestroyMenu( cloud_hmenu );
}
}
}
}
break;
}
}
switch ( l->code )
{
case HDN_ITEMCHANGING:
{
LPNMHEADERW phdr = (LPNMHEADERW)lParam;
if ( phdr->pitem && ( HDI_WIDTH & phdr->pitem->mask ) && phdr->iItem == 1 )
{
if ( !cloud_avail )
phdr->pitem->cxy = 0;
else
{
INT width = phdr->pitem->cxy;
if ( MLCloudColumn_GetWidth( plugin.hwndLibraryParent, &width ) )
phdr->pitem->cxy = width;
}
}
break;
}
}
return 0;
}

View File

@ -0,0 +1,32 @@
#ifndef NULLSOFT_ML_PLAYLISTS_PLAYLISTVIEW_H
#define NULLSOFT_ML_PLAYLISTS_PLAYLISTVIEW_H
#include "../nu/listview.h"
#define WM_PLAYLIST WM_USER + 1980
#define WM_PLAYLIST_RELOAD WM_PLAYLIST + 1
#define WM_PLAYLIST_UNLOAD WM_PLAYLIST + 2
extern int we_are_drag_and_dropping;
extern W_ListView playlist_list;
class PlayListView : public W_ListView
{
public:
};
// TODO: make a nice pretty class to wrap the playlist view
BOOL playlist_Notify( HWND hwndDlg, WPARAM wParam, LPARAM lParam );
void PlaySelection( int enqueue, int is_all );
void UpdatePlaylistTime( HWND hwndDlg );
void TagEditor( HWND hwnd );
void SyncPlaylist();
void Playlist_DeleteSelected( int selected );
void Playlist_ResetSelected();
void EditEntry( HWND parent );
void DownloadSelectedEntries( HWND hwndDlg );
#endif

View File

@ -0,0 +1,120 @@
#include "main.h"
#include "PlaylistsCB.h"
using namespace Nullsoft::Utility;
static UINT_PTR refreshTimer;
void CALLBACK RefreshPlaylistsCallback(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
if (idEvent == 777)
{
KillTimer(plugin.hwndWinampParent, idEvent);
refreshTimer = 0;
RefreshPlaylistsList();
}
}
int PlaylistsCB::notify(int msg, intptr_t param1, intptr_t param2)
{
switch (msg)
{
case api_playlists::PLAYLIST_ADDED:
{
AutoLockT<api_playlists> lock(AGAVE_API_PLAYLISTS); // should already be locked, but can't hurt
size_t newIndex = static_cast<size_t>(param1);
PlaylistInfo playlist(newIndex);
if (playlist.Valid())
{
// TODO: check where newIndex is, so we can insert into the middle of the list if necessary
/* TODO: maybe stuff this in Set/GetInfo somewhere
if (makeTree)
*/
MakeTree(playlist);
if (refreshTimer) KillTimer(plugin.hwndWinampParent, refreshTimer);
refreshTimer = SetTimer(plugin.hwndWinampParent, 777, 250, RefreshPlaylistsCallback);
if (!param2) AGAVE_API_PLAYLISTS->Flush(); // REVIEW: save immediately? or only at the end?
}
return 1;
}
break;
case api_playlists::PLAYLIST_REMOVED_PRE:
{
AutoLockT<api_playlists> lock(AGAVE_API_PLAYLISTS); // should already be locked, but can't hurt
size_t index = static_cast<size_t>(param1),
count = AGAVE_API_PLAYLISTS->GetCount();
PlaylistInfo info(index);
if (info.Valid())
{
// is a number of cases where this just seems to fail and so we revert to manually
// parsing the playlists tree inorder to find and remove the expected playlist by
// the index of the playlist (since we don't alter the order, this should be safe)
if (!info.treeId)
{
INT_PTR id = mediaLibrary.GetChildId(playlistsTreeId);
if (id)
{
for (size_t i = 0; i < count; i++)
{
if (i == index)
{
mediaLibrary.RemoveTreeItem(id);
break;
}
if (!(id = mediaLibrary.GetNextId(id))) break;
}
}
}
else
mediaLibrary.RemoveTreeItem(info.treeId);
tree_to_guid_map.erase(info.treeId);
}
return 1;
}
break;
case api_playlists::PLAYLIST_REMOVED_POST:
{
if (refreshTimer) KillTimer(plugin.hwndWinampParent, refreshTimer);
refreshTimer = SetTimer(plugin.hwndWinampParent, 777, 250, RefreshPlaylistsCallback);
return 1;
}
break;
case api_playlists::PLAYLIST_RENAMED:
{
AutoLockT<api_playlists> lock(AGAVE_API_PLAYLISTS); // should already be locked, but can't hurt
// tell the media library to rename the treeview node
size_t index = static_cast<size_t>(param1);
PlaylistInfo playlist(index);
if (playlist.Valid())
{
mediaLibrary.RenameTreeId(playlist.treeId, playlist.GetName());
}
return 1;
}
break;
case api_playlists::PLAYLIST_SAVED:
if (param2 != (intptr_t)&uniqueAddress)
{
AutoLockT<api_playlists> lock(AGAVE_API_PLAYLISTS); // should already be locked, but can't hurt
size_t index = static_cast<size_t>(param1);
playlist_ReloadGUID(AGAVE_API_PLAYLISTS->GetGUID(index));
}
break;
case api_playlists::PLAYLIST_FLUSH_REQUEST:
if (param2 != (intptr_t)&uniqueAddress)
{
AutoLockT<api_playlists> lock(AGAVE_API_PLAYLISTS); // should already be locked, but can't hurt
size_t index = static_cast<size_t>(param1);
playlist_SaveGUID(AGAVE_API_PLAYLISTS->GetGUID(index));
}
break;
}
return 0;
}
#define CBCLASS PlaylistsCB
START_DISPATCH;
CB(SYSCALLBACK_GETEVENTTYPE, getEventType);
CB(SYSCALLBACK_NOTIFY, notify);
END_DISPATCH;
#undef CBCLASS

View File

@ -0,0 +1,16 @@
#ifndef NULLSOFT_ML_PLAYLISTS_PLAYLISTSCB_H
#define NULLSOFT_ML_PLAYLISTS_PLAYLISTSCB_H
#include <api/syscb/callbacks/syscb.h>
#include "../playlist/api_playlists.h"
class PlaylistsCB : public SysCallback
{
public:
FOURCC getEventType() { return api_playlists::SYSCALLBACK; }
int notify(int msg, intptr_t param1 = 0, intptr_t param2 = 0);
protected:
RECVS_DISPATCH;
};
#endif

View File

@ -0,0 +1,178 @@
#include "main.h"
#include "PlaylistsCOM.h"
using namespace Nullsoft::Utility;
enum
{
DISP_PLALISTS_GETXML = 777,
};
#define CHECK_ID(str, id) if (wcscmp(rgszNames[i], L##str) == 0) { rgdispid[i] = id; continue; }
HRESULT PlaylistsCOM::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
{
bool unknowns = false;
for (unsigned int i = 0;i != cNames;i++)
{
CHECK_ID("GetXML", DISP_PLALISTS_GETXML)
rgdispid[i] = DISPID_UNKNOWN;
unknowns = true;
}
if (unknowns)
return DISP_E_UNKNOWNNAME;
else
return S_OK;
}
HRESULT PlaylistsCOM::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
{
return E_NOTIMPL;
}
HRESULT PlaylistsCOM::GetTypeInfoCount(unsigned int FAR * pctinfo)
{
return E_NOTIMPL;
}
static void WriteEscaped(FILE *fp, const wchar_t *str)
{
// TODO: for speed optimization,
// we should wait until we hit a special character
// and write out everything else so before it,
// like how ASX loader does it
while (str && *str)
{
switch (*str)
{
case L'&':
fputws(L"&amp;", fp);
break;
case L'>':
fputws(L"&gt;", fp);
break;
case L'<':
fputws(L"&lt;", fp);
break;
case L'\'':
fputws(L"&apos;", fp);
break;
case L'\"':
fputws(L"&quot;", fp);
break;
default:
fputwc(*str, fp);
break;
}
// write out the whole UTF-16 character
wchar_t *next = CharNextW(str);
while (++str != next)
fputwc(*str, fp);
}
}
static void SavePlaylistsXML(const wchar_t *destination, size_t limit)
{
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
FILE *fp = _wfopen(destination, L"wb");
fputws(L"\xFEFF", fp);
fwprintf(fp, L"<?xml version=\"1.0\" encoding=\"UTF-16\"?>");
size_t numPlaylists = limit ? min(limit, AGAVE_API_PLAYLISTS->GetCount()) : AGAVE_API_PLAYLISTS->GetCount();
fwprintf(fp, L"<playlists playlists=\"%lu\">", numPlaylists);
for (size_t i = 0; i != numPlaylists; i++)
{
fputws(L"<playlist filename=\"", fp);
WriteEscaped(fp, AGAVE_API_PLAYLISTS->GetFilename(i));
fputws(L"\" title=\"", fp);
WriteEscaped(fp, AGAVE_API_PLAYLISTS->GetName(i));
unsigned int numItems = 0, length = 0;
AGAVE_API_PLAYLISTS->GetInfo(i, api_playlists_itemCount, &numItems, sizeof(numItems));
AGAVE_API_PLAYLISTS->GetInfo(i, api_playlists_totalTime, &length, sizeof(length));
fwprintf(fp, L"\" songs=\"%u\" seconds=\"%u\"/>",
numItems,
length);
}
fwprintf(fp, L"</playlists>");
fclose(fp);
}
HRESULT PlaylistsCOM::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 DISP_PLALISTS_GETXML:
{
int max = 0;
if (pdispparams->cArgs == 1)
{
max = _wtoi(pdispparams->rgvarg[0].bstrVal);
}
wchar_t tempPath[MAX_PATH] = {0};
GetTempPathW(MAX_PATH, tempPath);
wchar_t tempFile[MAX_PATH] = {0};
GetTempFileNameW(tempPath, L"mpx", 0, tempFile);
SavePlaylistsXML(tempFile, max);
HANDLE plFile = CreateFile(tempFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
size_t flen = SetFilePointer(plFile, 0, NULL, FILE_END);
SetFilePointer(plFile, 0, NULL, FILE_BEGIN);
SAFEARRAY *bufferArray = SafeArrayCreateVector(VT_UI1, 0, flen);
void *data;
SafeArrayAccessData(bufferArray, &data);
DWORD bytesRead = 0;
ReadFile(plFile, data, flen, &bytesRead, 0);
SafeArrayUnaccessData(bufferArray);
CloseHandle(plFile);
DeleteFile(tempFile);
VariantInit(pvarResult);
V_VT(pvarResult) = VT_ARRAY | VT_UI1;
V_ARRAY(pvarResult) = bufferArray;
}
return S_OK;
}
return DISP_E_MEMBERNOTFOUND;
}
STDMETHODIMP PlaylistsCOM::QueryInterface(REFIID riid, PVOID *ppvObject)
{
if (!ppvObject)
return E_POINTER;
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 PlaylistsCOM::AddRef(void)
{
return 0;
}
ULONG PlaylistsCOM::Release(void)
{
return 0;
}

View File

@ -0,0 +1,20 @@
#ifndef NULLSOFT_PLAYLISTSCOM_H
#define NULLSOFT_PLAYLISTSCOM_H
#include <ocidl.h>
class PlaylistsCOM : public IDispatch
{
public:
STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject);
STDMETHOD_(ULONG, AddRef)(void);
STDMETHOD_(ULONG, Release)(void);
// *** IDispatch Methods ***
STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid);
STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo);
STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo);
STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr);
};
#endif

View File

@ -0,0 +1,72 @@
#include "main.h"
#include "resource.h"
using namespace Nullsoft::Utility;
static INT_PTR CALLBACK RenamePlaylistDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static GUID playlist_guid = INVALID_GUID;
switch (uMsg)
{
case WM_INITDIALOG:
{
playlist_guid = *(GUID *)lParam;
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
PlaylistInfo playlist(playlist_guid);
if (playlist.Valid())
{
size_t index = playlist.GetIndex();
const wchar_t *title = AGAVE_API_PLAYLISTS->GetName(index);
SetDlgItemText(hwndDlg, IDC_NAME, title);
SendMessage(GetDlgItem(hwndDlg, IDC_NAME), EM_SETSEL, 0, -1);
SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_NAME), TRUE);
}
else
EndDialog(hwndDlg, 1);
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
{
wchar_t name[1024] = {0};
GetDlgItemText(hwndDlg, IDC_NAME, name, 1023);
name[1023] = 0;
if (!name[0])
{
wchar_t titleStr[32] = {0};
MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_ENTER_A_NAME),
WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,titleStr,32), MB_OK);
break;
}
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
PlaylistInfo playlist(playlist_guid);
if (playlist.Valid())
{
size_t index = playlist.GetIndex();
AGAVE_API_PLAYLISTS->RenamePlaylist(index, name);
AGAVE_API_PLAYLISTS->Flush(); // TODO: save immediately? or only at the end?
}
EndDialog(hwndDlg, 1);
}
break;
case IDCANCEL:
EndDialog(hwndDlg, 0);
break;
}
break;
}
return FALSE;
};
void RenamePlaylist(GUID _guid, HWND parent)
{
WASABI_API_DIALOGBOXPARAMW(IDD_RENAME_PLAYLIST, parent, RenamePlaylistDialogProc, (LPARAM)&_guid);
}

View File

@ -0,0 +1,697 @@
#include <strsafe.h>
#include "main.h"
#include "api__ml_playlists.h"
#include "Playlist.h"
#include "PlaylistView.h"
#include "resource.h"
#include "../nu/AutoWideFn.h"
#include <vector>
/* TODO:
Somehow replace index values in send-to with GUID's. Possibly by making a vector of GUIDs (like view_playlists).
Since index values could (in theory) change in a background thread while send-to interaction is going on.
*/
extern Playlist currentPlaylist;
using namespace Nullsoft::Utility;
static std::vector<GUID> sendto_playlistGUIDs;
int playlists_CloudAvailable()
{
if (IPC_GET_CLOUD_HINST == -1) IPC_GET_CLOUD_HINST = (INT)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"WinampCloud", IPC_REGISTER_WINAMP_IPCMESSAGE);
if (IPC_GET_CLOUD_ACTIVE == -1) IPC_GET_CLOUD_ACTIVE = (INT)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"WinampCloudActive", IPC_REGISTER_WINAMP_IPCMESSAGE);
if (!cloud_hinst) cloud_hinst = (HINSTANCE)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_HINST);
return (cloud_avail = /*0/*/!(!cloud_hinst || cloud_hinst == (HINSTANCE)1 || !SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE))/**/);
}
int playlists_CloudInstalled()
{
if (IPC_GET_CLOUD_HINST == -1) IPC_GET_CLOUD_HINST = (INT)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"WinampCloud", IPC_REGISTER_WINAMP_IPCMESSAGE);
if (!cloud_hinst) cloud_hinst = (HINSTANCE)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_HINST);
return (!(!cloud_hinst || cloud_hinst == (HINSTANCE)1));
}
int playlists_BuildSendTo(int sourceType, INT_PTR context)
{
if (sourceType == ML_TYPE_ITEMRECORDLISTW || sourceType == ML_TYPE_ITEMRECORDLIST ||
sourceType == ML_TYPE_FILENAMES || sourceType == ML_TYPE_STREAMNAMES ||
sourceType == ML_TYPE_FILENAMESW || sourceType == ML_TYPE_STREAMNAMESW ||
sourceType == ML_TYPE_CDTRACKS)
{
if (g_config->ReadInt(L"pl_send_to", DEFAULT_PL_SEND_TO))
{
mediaLibrary.BranchSendTo(context);
mediaLibrary.AddToSendTo(WASABI_API_LNGSTRINGW(IDS_SENDTO_NEW_PLAYLIST), context, playlistsTreeId);
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
sendto_playlistGUIDs.clear();
size_t count = AGAVE_API_PLAYLISTS->GetCount();
for (size_t i = 0;i != count;i++)
{
PlaylistInfo info(i);
if (info.Valid())
{
sendto_playlistGUIDs.push_back(info.playlist_guid);
if (sendToIgnoreID != info.treeId)
{
mediaLibrary.AddToBranchSendTo(info.GetName(), context, reinterpret_cast<INT_PTR>(pluginMessageProc) + i);
}
}
}
mediaLibrary.EndBranchSendTo(WASABI_API_LNGSTRINGW(IDS_SENDTO_ML_PLAYLISTS), context);
}
else
{
mediaLibrary.AddToSendTo(WASABI_API_LNGSTRINGW(IDS_SENDTO_PLAYLIST), context, playlistsTreeId);
}
}
return 0;
}
static void NewPlaylist( Playlist &newPlaylist, const wchar_t *playlistTitle, int makeTree, const wchar_t *newFilename = 0 )
{
if ( playlistTitle && *playlistTitle )
{
size_t numItems = newPlaylist.GetNumItems();
wchar_t filename[ 1024 + 256 ] = { 0 };
if ( newFilename )
{
if ( PathIsFileSpecW( newFilename ) )
PathCombineW( filename, g_path, newFilename );
else
lstrcpynW( filename, newFilename, MAX_PATH );
}
else
PathCombineW( filename, g_path, createPlayListDBFileName( filename ) );
AGAVE_API_PLAYLISTMANAGER->Save( filename, &newPlaylist );
AddPlaylist( true, playlistTitle, filename, ( LOWORD( makeTree ) != 0 ), HIWORD( makeTree ), numItems, newPlaylist.lengthInMS );
}
else
{
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
size_t a = AGAVE_API_PLAYLISTS->GetCount();
playlists_Add( plugin.hwndLibraryParent, false );
if ( AGAVE_API_PLAYLISTS->GetCount() == a + 1 )
{
PlaylistInfo info( a );
if ( info.Valid() )
{
info.SetLength( (int)( newPlaylist.lengthInMS > 0 ? newPlaylist.lengthInMS / 1000 : 0 ) );
info.SetSize( newPlaylist.GetNumItems() );
info.SetCloud( HIWORD( makeTree ) );
wchar_t fn[ MAX_PATH ] = { 0 };
if ( PathIsFileSpecW( info.GetFilename() ) )
PathCombineW( fn, g_path, info.GetFilename() );
else
lstrcpynW( fn, info.GetFilename(), MAX_PATH );
for ( pl_entry *l_entry : newPlaylist.entries )
{
for ( pl_entry *l_current_entry : currentPlaylist.entries )
{
if ( wcscmp( l_entry->filename, l_current_entry->filename ) == 0 )
{
//SetTitle releases previously allocated string, no need to free previous one
l_entry->SetTitle(l_current_entry->filetitle);
if ( !l_current_entry->_extended_infos.empty() )
{
for ( auto l_current_extended_info : l_current_entry->_extended_infos )
l_entry->_extended_infos.emplace( _wcsdup( l_current_extended_info.first.c_str() ), _wcsdup( l_current_extended_info.second.c_str() ) );
}
break;
}
}
}
AGAVE_API_PLAYLISTMANAGER->Save( fn, &newPlaylist );
// delay the added notification being sent so for the cloud
// we can have the complete playlist available to work with
AGAVE_API_PLAYLISTS->Flush();
WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_ADDED, a/* + 1*/, 0 );
UpdateTree( info, info.treeId );
}
}
}
RefreshPlaylistsList();
}
void AddPlaylistFromFilenames(const char *filenames, const wchar_t *playlistTitle, int makeTree, const wchar_t *filename)
{
Playlist newPlaylist;
while (filenames && *filenames)
{
AutoWide wideFn(filenames);
if (AGAVE_API_PLAYLISTMANAGER->Load(wideFn, &newPlaylist) != PLAYLISTMANAGER_SUCCESS) // try to load it as a playlist first
{
wchar_t title[FILETITLE_SIZE] = {0};
int length = 0;
mediaLibrary.GetFileInfo(wideFn, title, FILETITLE_SIZE, &length);
newPlaylist.AppendWithInfo(wideFn, title, length*1000); // otherwise just add it to the playlist directly
}
filenames += strlen(filenames) + 1;
}
NewPlaylist(newPlaylist, playlistTitle, makeTree, filename);
}
void AddPlaylistFromFilenamesW(const wchar_t *filenames, const wchar_t *playlistTitle, int makeTree, const wchar_t *filename)
{
Playlist newPlaylist;
while (filenames && *filenames)
{
if (AGAVE_API_PLAYLISTMANAGER->Load(filenames, &newPlaylist) != PLAYLISTMANAGER_SUCCESS) // try to load it as a playlist first
{
wchar_t title[FILETITLE_SIZE] = {0};
int length = 0;
mediaLibrary.GetFileInfo(filenames, title, FILETITLE_SIZE, &length);
newPlaylist.AppendWithInfo(filenames, title, (length > 0 ? length*1000 : 0)); // otherwise just add it to the playlist directly
}
filenames += wcslen(filenames) + 1;
}
NewPlaylist(newPlaylist, playlistTitle, makeTree, filename);
}
static wchar_t *itemrecordTagFunc(wchar_t *tag, void * p) //return 0 if not found
{
itemRecord *t = (itemRecord *)p;
char buf[128] = {0};
char *value = NULL;
if (!_wcsicmp(tag, L"artist")) value = t->artist;
else if (!_wcsicmp(tag, L"album")) value = t->album;
else if (!_wcsicmp(tag, L"filename")) value = t->filename;
else if (!_wcsicmp(tag, L"title")) value = t->title;
else if (!_wcsicmp(tag, L"ext")) value = t->ext;
else if (!_wcsicmp(tag, L"year"))
{
if (t->year > 0)
{
StringCchPrintfA(buf, 128, "%04d", t->year);
value = buf;
}
}
else if (!_wcsicmp(tag, L"genre")) value = t->genre;
else if (!_wcsicmp(tag, L"comment")) value = t->comment;
else if (!_wcsicmp(tag, L"tracknumber") || !_wcsicmp(tag, L"track"))
{
if (t->track > 0)
{
StringCchPrintfA(buf, 128, "%02d", t->track);
value = buf;
}
}
else if (!_wcsicmp(tag, L"rating")) value = getRecordExtendedItem(t, "RATING");
else if (!_wcsicmp(tag, L"playcount")) value = getRecordExtendedItem(t, "PLAYCOUNT");
else if (!_wcsicmp(tag, L"bitrate")) value = getRecordExtendedItem(t, "BITRATE");
else
return 0;
if (!value) return reinterpret_cast<wchar_t *>(-1);
else return AutoWideDup(value);
}
static wchar_t *itemrecordWTagFunc(wchar_t *tag, void * p) //return 0 if not found
{
itemRecordW *t = (itemRecordW *)p;
wchar_t buf[128] = {0};
wchar_t *value = NULL;
// TODO: more fields
if (!_wcsicmp(tag, L"artist")) value = t->artist;
else if (!_wcsicmp(tag, L"album")) value = t->album;
else if (!_wcsicmp(tag, L"filename")) value = t->filename;
else if (!_wcsicmp(tag, L"title")) value = t->title;
else if (!_wcsicmp(tag, L"year"))
{
if (t->year > 0)
{
StringCchPrintfW(buf, 128, L"%04d", t->year);
value = buf;
}
}
else if (!_wcsicmp(tag, L"genre")) value = t->genre;
else if (!_wcsicmp(tag, L"comment")) value = t->comment;
else if (!_wcsicmp(tag, L"tracknumber") || !_wcsicmp(tag, L"track"))
{
if (t->track > 0)
{
StringCchPrintfW(buf, 128, L"%02d", t->track);
value = buf;
}
}
else if (!_wcsicmp(tag, L"rating"))
{
if (t->rating > 0)
{
StringCchPrintfW(buf, 128, L"%d", t->rating);
value = buf;
}
}
else if (!_wcsicmp(tag, L"playcount")) value = t->comment;
else if (!_wcsicmp(tag, L"bitrate"))
{
if (t->bitrate > 0)
{
StringCchPrintfW(buf, 128, L"%d", t->bitrate);
value = buf;
}
}
else
return 0;
if (!value) return reinterpret_cast<wchar_t *>(-1);
else return _wcsdup(value);
}
static void fieldTagFuncFree(char * tag, void * p)
{
free(tag);
}
static void BuildTitle(itemRecord *record, wchar_t *title, int lenCch)
{
AutoWideFn wfn(record->filename);
waFormatTitleExtended fmt;
fmt.filename = wfn;
fmt.useExtendedInfo = 1;
fmt.out = title;
fmt.out_len = lenCch;
fmt.p = record;
fmt.spec = 0;
*(void **)&fmt.TAGFUNC = itemrecordTagFunc;
*(void **)&fmt.TAGFREEFUNC = fieldTagFuncFree;
*title = 0;
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&fmt, IPC_FORMAT_TITLE_EXTENDED);
}
static void BuildTitleW(itemRecordW *record, wchar_t *title, int lenCch)
{
waFormatTitleExtended fmt;
fmt.filename = record->filename;
fmt.useExtendedInfo = 1;
fmt.out = title;
fmt.out_len = lenCch;
fmt.p = record;
fmt.spec = 0;
*(void **)&fmt.TAGFUNC = itemrecordWTagFunc;
*(void **)&fmt.TAGFREEFUNC = fieldTagFuncFree;
*title = 0;
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&fmt, IPC_FORMAT_TITLE_EXTENDED);
}
void AddPlaylistFromItemRecordList(itemRecordList *obj, const wchar_t *playlistTitle, int makeTree, const wchar_t *filename)
{
Playlist newPlaylist;
wchar_t title[FILETITLE_SIZE] = {0};
for (int x = 0; x < obj->Size; x ++)
{
BuildTitle(&obj->Items[x], title, FILETITLE_SIZE);
newPlaylist.AppendWithInfo(AutoWide(obj->Items[x].filename), title, obj->Items[x].length*1000);
}
NewPlaylist(newPlaylist, playlistTitle, makeTree, filename);
}
void AddPlaylistFromItemRecordListW(itemRecordListW *obj, const wchar_t *playlistTitle, int makeTree, const wchar_t *filename)
{
Playlist newPlaylist;
wchar_t title[FILETITLE_SIZE] = {0};
for (int x = 0; x < obj->Size; x ++)
{
BuildTitleW(&obj->Items[x], title, FILETITLE_SIZE);
newPlaylist.AppendWithInfo(obj->Items[x].filename, title, obj->Items[x].length*1000);
}
NewPlaylist(newPlaylist, playlistTitle, makeTree, filename);
}
static void AddToPlaylist(GUID playlist_guid, int sourceType, INT_PTR data)
{
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
playlist_Save(plugin.hwndWinampParent);
PlaylistInfo p(playlist_guid);
if (p.Valid())
{
Playlist playlist;
AGAVE_API_PLAYLISTMANAGER->Load(p.GetFilename(), &playlist);
if (sourceType == ML_TYPE_FILENAMES || sourceType == ML_TYPE_STREAMNAMES)
{
const char *ptr = (const char*)data;
while (ptr && *ptr)
{
AutoWide wideFn(ptr);
// try to load in playlist manager first
Playlist sentPlaylist;
if (sourceType == ML_TYPE_FILENAMES
&& AGAVE_API_PLAYLISTMANAGER->Load(wideFn, &sentPlaylist) == PLAYLISTMANAGER_SUCCESS)
{
playlist.AppendPlaylist(sentPlaylist);
}
else
{
wchar_t title[FILETITLE_SIZE] = {0};
int length = -1;
mediaLibrary.GetFileInfo(wideFn, title, FILETITLE_SIZE, &length);
playlist.AppendWithInfo(wideFn, title, length*1000);
}
ptr += strlen(ptr) + 1;
}
}
else if (sourceType == ML_TYPE_FILENAMESW || sourceType == ML_TYPE_STREAMNAMESW)
{
const wchar_t *ptr = (const wchar_t*)data;
while (ptr && *ptr)
{
// try to load in playlist manager first
Playlist sentPlaylist;
if (sourceType == ML_TYPE_FILENAMES
&& AGAVE_API_PLAYLISTMANAGER->Load(ptr, &sentPlaylist) == PLAYLISTMANAGER_SUCCESS)
{
playlist.AppendPlaylist(sentPlaylist);
}
else
{
wchar_t title[FILETITLE_SIZE] = {0};
int length = -1;
mediaLibrary.GetFileInfo(ptr, title, FILETITLE_SIZE, &length);
std::map<std::wstring, std::wstring> l_extended_infos;
if ( PathIsURLW( title ) )
{
wchar_t *end = 0;
for ( pl_entry *l_current_entry : currentPlaylist.entries )
{
if ( wcscmp( title, l_current_entry->filename ) == 0 )
{
StringCchCopyExW( title, FILETITLE_SIZE, l_current_entry->filetitle, &end, 0, 0 );
if ( !l_current_entry->_extended_infos.empty() )
{
for ( auto l_current_extended_info : l_current_entry->_extended_infos )
l_extended_infos.emplace( _wcsdup( l_current_extended_info.first.c_str() ), _wcsdup( l_current_extended_info.second.c_str() ) );
}
break;
}
}
}
if ( l_extended_infos.empty() )
playlist.AppendWithInfo( ptr, title, length * 1000 );
else
playlist.AppendWithInfo( ptr, title, length * 1000, l_extended_infos );
}
ptr += wcslen(ptr) + 1;
}
}
else if (sourceType == ML_TYPE_ITEMRECORDLIST || sourceType == ML_TYPE_CDTRACKS)
{
itemRecordList *obj = (itemRecordList *)data;
wchar_t title[FILETITLE_SIZE] = {0};
for (int x = 0; x < obj->Size; x ++)
{
BuildTitle(&obj->Items[x], title, FILETITLE_SIZE);
playlist.AppendWithInfo(AutoWide(obj->Items[x].filename), title, obj->Items[x].length*1000);
}
}
else if (sourceType == ML_TYPE_ITEMRECORDLISTW)
{
itemRecordListW *obj = (itemRecordListW *)data;
wchar_t title[FILETITLE_SIZE] = {0};
for (int x = 0; x < obj->Size; x ++)
{
BuildTitleW(&obj->Items[x], title, FILETITLE_SIZE);
playlist.AppendWithInfo(obj->Items[x].filename, title, obj->Items[x].length*1000);
}
}
else if (sourceType == ML_TYPE_PLAYLIST)
{
mlPlaylist *ptr = (mlPlaylist *)data;
Playlist sentPlaylist;
if (AGAVE_API_PLAYLISTMANAGER->Load(ptr->filename, &sentPlaylist) == PLAYLISTMANAGER_SUCCESS)
{
playlist.AppendPlaylist(sentPlaylist);
}
}
else if (sourceType == ML_TYPE_PLAYLISTS)
{
mlPlaylist **playlists= (mlPlaylist **)data;
while(playlists && *playlists)
{
mlPlaylist *ptr = *playlists;
Playlist sentPlaylist;
if (AGAVE_API_PLAYLISTMANAGER->Load(ptr->filename, &sentPlaylist) == PLAYLISTMANAGER_SUCCESS)
{
playlist.AppendPlaylist(sentPlaylist);
}
playlists++;
}
}
AGAVE_API_PLAYLISTMANAGER->Save(p.GetFilename(), &playlist);
p.SetSize(playlist.GetNumItems());
p.IssueSaveCallback();
playlist_Reload();
}
}
int playlists_OnDropTarget(int id, int sourceType, INT_PTR data)
{
if (id == playlistsTreeId)
{
switch (sourceType)
{
case ML_TYPE_FILENAMES:
case ML_TYPE_STREAMNAMES:
if (data)
AddPlaylistFromFilenames((const char *)data, 0, 1);
return 1;
case ML_TYPE_FILENAMESW:
case ML_TYPE_STREAMNAMESW:
if (data)
AddPlaylistFromFilenamesW((const wchar_t *)data, 0, 1);
return 1;
case ML_TYPE_ITEMRECORDLIST:
case ML_TYPE_CDTRACKS:
if (data)
AddPlaylistFromItemRecordList((itemRecordList *)data, 0, 1);
return 1;
case ML_TYPE_ITEMRECORDLISTW:
if (data)
AddPlaylistFromItemRecordListW((itemRecordListW *)data, 0, 1);
return 1;
default:
return -1;
}
}
else if (FindTreeItem(id))
{
switch (sourceType)
{
case ML_TYPE_FILENAMES:
case ML_TYPE_STREAMNAMES:
case ML_TYPE_FILENAMESW:
case ML_TYPE_STREAMNAMESW:
case ML_TYPE_ITEMRECORDLIST:
case ML_TYPE_ITEMRECORDLISTW:
case ML_TYPE_CDTRACKS:
if (data && !we_are_drag_and_dropping)
{
AddToPlaylist(tree_to_guid_map[id], sourceType, data);
}
return 1;
default:
return -1;
}
}
return 0;
}
static INT_PTR CALLBACK SelectPlaylistProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
{
LRESULT index = SendDlgItemMessage(hwndDlg, IDC_PLAYLISTS, CB_ADDSTRING, 0, (LPARAM) WASABI_API_LNGSTRINGW(IDS_SENDTO_NEW_PLAYLIST));
SendDlgItemMessage(hwndDlg, IDC_PLAYLISTS, CB_SETITEMDATA , index, -1);
/*if (playlists_CloudAvailable())
{
index = SendDlgItemMessage(hwndDlg, IDC_PLAYLISTS, CB_ADDSTRING, 0, (LPARAM) WASABI_API_LNGSTRINGW(IDS_SENDTO_NEW_CLOUD_PLAYLIST));
SendDlgItemMessage(hwndDlg, IDC_PLAYLISTS, CB_SETITEMDATA , index, -1);
}*/
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
sendto_playlistGUIDs.clear();
size_t count = AGAVE_API_PLAYLISTS->GetCount();
for (size_t i = 0;i != count; i++)
{
PlaylistInfo info(i);
if (info.Valid())
{
sendto_playlistGUIDs.push_back(info.playlist_guid);
if (sendToIgnoreID != info.treeId)
{
index = SendDlgItemMessage(hwndDlg, IDC_PLAYLISTS, CB_ADDSTRING, 0, (LPARAM)info.GetName());
SendDlgItemMessage(hwndDlg, IDC_PLAYLISTS, CB_SETITEMDATA , index, i);
}
}
}
SendDlgItemMessage(hwndDlg, IDC_PLAYLISTS, CB_SETCURSEL, 0, 0);
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
{
int selection = SendDlgItemMessage(hwndDlg, IDC_PLAYLISTS, CB_GETCURSEL, 0, 0);
if (selection != CB_ERR)
EndDialog(hwndDlg, SendDlgItemMessage(hwndDlg, IDC_PLAYLISTS, CB_GETITEMDATA , selection, 0));
else
EndDialog(hwndDlg, -2);
}
break;
case IDCANCEL:
{
EndDialog(hwndDlg, -2);
}
break;
}
break;
}
return 0;
}
int playlists_OnSendTo(int sourceType, INT_PTR data, int id)
{
if (id == playlistsTreeId || id == playlistsCloudTreeId)
{
if (g_config->ReadInt(L"pl_send_to", DEFAULT_PL_SEND_TO))
{
int is_cloud = (id == playlistsCloudTreeId) || (id == playlistsTreeId && g_config->ReadInt(L"cloud", 1));
switch (sourceType)
{
case ML_TYPE_FILENAMES:
case ML_TYPE_STREAMNAMES:
AddPlaylistFromFilenames((const char *)data, 0, MAKELONG(1, is_cloud));
return 1;
case ML_TYPE_FILENAMESW:
case ML_TYPE_STREAMNAMESW:
AddPlaylistFromFilenamesW((const wchar_t *)data, 0, MAKELONG(1, is_cloud));
return 1;
case ML_TYPE_ITEMRECORDLIST:
case ML_TYPE_CDTRACKS:
AddPlaylistFromItemRecordList((itemRecordList *)data, 0, MAKELONG(1, is_cloud));
return 1;
case ML_TYPE_ITEMRECORDLISTW:
AddPlaylistFromItemRecordListW((itemRecordListW *)data, 0, MAKELONG(1, is_cloud));
return 1;
case ML_TYPE_PLAYLIST:
{
mlPlaylist *ptr = (mlPlaylist *)data;
AddPlaylistFromFilenamesW(ptr->filename, 0, MAKELONG(1, is_cloud));
return 1;
}
case ML_TYPE_PLAYLISTS:
{
mlPlaylist **playlists= (mlPlaylist **)data;
while(playlists && *playlists)
{
mlPlaylist *ptr = *playlists;
AddPlaylistFromFilenamesW(ptr->filename, 0, MAKELONG(1, is_cloud));
playlists++;
}
return 1;
}
}
}
else
{
size_t selection = WASABI_API_DIALOGBOXW(IDD_SELECT_PLAYLIST, NULL, SelectPlaylistProc);
if (selection == -2)
return -1;
else if (selection == -1)
{
switch (sourceType)
{
case ML_TYPE_FILENAMES:
case ML_TYPE_STREAMNAMES:
AddPlaylistFromFilenames((const char *)data, 0, true);
return 1;
case ML_TYPE_FILENAMESW:
case ML_TYPE_STREAMNAMESW:
AddPlaylistFromFilenamesW((const wchar_t *)data, 0, true);
return 1;
case ML_TYPE_ITEMRECORDLIST:
case ML_TYPE_CDTRACKS:
AddPlaylistFromItemRecordList((itemRecordList *)data, 0, 1);
return 1;
case ML_TYPE_ITEMRECORDLISTW:
AddPlaylistFromItemRecordListW((itemRecordListW *)data, 0, 1);
return 1;
case ML_TYPE_PLAYLIST:
{
mlPlaylist *ptr = (mlPlaylist *)data;
AddPlaylistFromFilenamesW(ptr->filename, 0, true);
return 1;
}
case ML_TYPE_PLAYLISTS:
{
mlPlaylist **playlists= (mlPlaylist **)data;
while(playlists && *playlists)
{
mlPlaylist *ptr = *playlists;
AddPlaylistFromFilenamesW(ptr->filename, 0, true);
playlists++;
}
return 1;
}
}
}
else if (selection >= 0 && selection < sendto_playlistGUIDs.size())
{
AddToPlaylist(sendto_playlistGUIDs[selection], sourceType, data);
return 1;
}
}
}
else
{
size_t x = id - reinterpret_cast<size_t>(pluginMessageProc);
if (x < sendto_playlistGUIDs.size())
{
AddToPlaylist(sendto_playlistGUIDs[x], sourceType, data);
return 1;
}
}
return 0;
}

View File

@ -0,0 +1,100 @@
#ifndef NULLSOFT_SEND_TO_HELPER_CLASS_H
#define NULLSOFT_SEND_TO_HELPER_CLASS_H
#include "../../General/gen_ml/ml_ipc.h"
#include "main.h"
#include <strsafe.h>
class SendToMenu
{
public:
SendToMenu()
{
memset(&sendTo, 0, sizeof(sendTo));
}
void AddHere(HWND hwnd, HMENU hMenu, int type, int simple = 0, int trueSourceType = 0)
{
sendTo.mode = 0;
sendTo.hwnd = 0;
sendTo.build_hMenu = 0;
IPC_LIBRARY_SENDTOMENU = SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"LibrarySendToMenu", IPC_REGISTER_WINAMP_IPCMESSAGE);
if (IPC_LIBRARY_SENDTOMENU > 65536 && SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)0, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
{
sendTo.mode = 1;
sendTo.hwnd = hwnd;
sendTo.data_type = type; //ML_TYPE_ITEMRECORDLIST;
sendTo.ctx[1] = simple;
sendTo.ctx[2] = trueSourceType;
sendTo.build_hMenu = hMenu;
}
}
bool WasClicked(int popUpReturnVal)
{
if (sendTo.mode == 2)
{
sendTo.menu_id = popUpReturnVal;
if (SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
return true;
}
return false;
}
void Cleanup()
{
if (sendTo.mode)
{
sendTo.mode = 4;
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU); // cleanup
}
sendTo.build_hMenu = 0;
}
bool InitPopupMenu(WPARAM wParam)
{
if (wParam && (HMENU)wParam == sendTo.build_hMenu && sendTo.mode == 1)
{
if (SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
sendTo.mode = 2;
return true;
}
return false;
}
// still need to free it on your own
void SendItemRecordList(itemRecordList *obj)
{
sendTo.data_type = ML_TYPE_ITEMRECORDLIST;
sendTo.mode = 3;
sendTo.data = (void*) & obj;
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU);
}
void SendFilenames(const wchar_t *filenames)
{
sendTo.data_type = ML_TYPE_FILENAMESW;
sendTo.mode = 3;
sendTo.data = (void*)filenames;
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU);
}
LRESULT SendPlaylist(mlPlaylist *playlist)
{
sendTo.data_type = ML_TYPE_PLAYLIST;
sendTo.mode = 3;
sendTo.data = (void*)playlist;
return SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU);
}
LRESULT SendPlaylists(mlPlaylist **playlists)
{
sendTo.data_type = ML_TYPE_PLAYLISTS;
sendTo.mode = 3;
sendTo.data = (void*)playlists;
return SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU);
}
private:
librarySendToMenuStruct sendTo;
};
#endif

View File

@ -0,0 +1,27 @@
#ifndef NULLSOFT_API_H
#define NULLSOFT_API_H
#include "api/application/api_application.h"
#define WASABI_API_APP applicationApi
#include "api/service/api_service.h"
#define WASABI_API_SVC serviceApi
#include "api/syscb/api_syscb.h"
#define WASABI_API_SYSCB sysCallbackApi
#include "../playlist/api_playlists.h"
extern api_playlists *playlistsApi;
#define AGAVE_API_PLAYLISTS playlistsApi
#include "../playlist/api_playlistmanager.h"
extern api_playlistmanager *playlistManager;
#define AGAVE_API_PLAYLISTMANAGER playlistManager
#include "../Components/wac_downloadManager/wac_downloadManager_api.h"
#include "../Agave/Language/api_language.h"
#include "../Agave/ExplorerFindFile/api_explorerfindfile.h"
#endif

View File

@ -0,0 +1,148 @@
#ifndef NULLSOFT_ML_PLAYLISTS_MAIN_H
#define NULLSOFT_ML_PLAYLISTS_MAIN_H
#include <windows.h>
#include <shlwapi.h>
#include "resource.h"
#include "../../Plugins/General/gen_ml/ml.h"
#include "../../Plugins/General/gen_ml/ml_ipc.h"
#include "../../Plugins/General/gen_ml/ml_ipc_0313.h"
#include "Winamp/wa_ipc.h"
#include "../../Plugins/General/gen_ml/config.h"
#include "PlaylistInfo.h"
#include "nu/MediaLibraryInterface.h"
#include "../../Plugins/General/gen_ml/menu.h"
#include "replicant/nu/AutoWide.h"
#include "replicant/nu/AutoChar.h"
#include "nu/DialogSkinner.h"
#include "api__ml_playlists.h"
#include "..\..\..\nu\AutoLock.h"
#include "ml_downloads/DownloadStatus.h"
#include "ml_downloads/DownloadsDialog.h"
#include "ml_downloads/Downloaded.h"
#include <map>
#define WINAMP_MANAGEPLAYLISTS 40385
#define ID_DOSHITMENU_ADDNEWPLAYLIST 40031
#ifndef FILENAME_SIZE
#define FILENAME_SIZE (MAX_PATH * 4)
#endif
INT_PTR pluginMessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3);
extern INT_PTR playlistsTreeId, playlistsCloudTreeId;
extern HNAVITEM playlistItem;
extern int imgPL, imgCloudPL;
extern wchar_t current_playing[FILENAME_SIZE];
INT_PTR LoadPlaylist(INT_PTR treeId);
void Playlists_ReplaceBadPathChars(LPWSTR pszPath);
extern HMENU wa_playlists_cmdmenu;
void HookPlaylistEditor();
void UnhookPlaylistEditor();
void UpdateMenuItems(HWND hwndDlg, HMENU menu);
INT_PTR CALLBACK view_playlistsDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK AddPlaylistDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK view_playlistDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
extern winampMediaLibraryPlugin plugin;
extern HMENU g_context_menus, g_context_menus2, g_context_menus3;
extern wchar_t g_path[MAX_PATH];
static const bool ADD_TO_TREE = true;
extern HINSTANCE cloud_hinst;
extern int IPC_GET_CLOUD_HINST, IPC_GET_CLOUD_ACTIVE;
extern int cloud_avail, normalimage, cloudImage, groupBtn, customAllowed, enqueuedef;
extern HWND currentView;
void AddPlaylist( int callback, const wchar_t *title, const wchar_t *filename, bool makeTree, int cloud, size_t numItems = 0, uint64_t length = 0 );
void LoadPlaylists();
void UpdatePlaylists();
int AddToCloud();
void playlists_Add(HWND parent, bool callback = true);
void playlists_AddToCloudPrompt(HWND hwndDlg);
enum { SORT_TITLE_ASCENDING, SORT_TITLE_DESCENDING, SORT_NUMBER_ASCENDING, SORT_NUMBER_DESCENDING };
void playlists_Sort(size_t sort_type);
wchar_t *createPlayListDBFileName(wchar_t *filename); // filename is ignored but used for temp space, make sure it's 1024+256 chars =)
void Playlist_importFromWinamp();
void Playlist_importFromFile(HWND dlgparent);
void Playlist_importFromFolders(HWND dlgparent);
bool FindTreeItem(INT_PTR treeId);
void RenamePlaylist(GUID _guid, HWND parent);
void DeletePlaylist(GUID _guid, HWND parent, bool confirm);
void RefreshPlaylistsList();
void HookMediaLibrary();
void UnhookMediaLibrary();
void playlist_ContextMenu(HWND hwndDlg, POINT p);
extern C_Config *g_config;
extern int (*warand)(void);
void MakeTree(PlaylistInfo &playlist);
void UpdateTree(PlaylistInfo &playlist, int tree_id);
LRESULT pluginHandleIpcMessage(int msg, WPARAM param);
extern HCURSOR hDragNDropCursor;
void FormatLength(wchar_t *str, int length, int buf_len);
extern int IPC_LIBRARY_SENDTOMENU;
void Hook(HWND winamp);
extern INT_PTR sendToIgnoreID;
extern INT_PTR lastActiveID;
void playlist_Reload(bool forced = false);
void playlist_Save(HWND hwndDlg);
void playlist_SaveGUID(GUID _guid);
void playlist_ReloadGUID(GUID _guid);
extern HWND activeHWND; // active playlist view
int CALLBACK WINAPI _bcp( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData);
INT_PTR PlayPlaylist(INT_PTR treeId);
INT_PTR playlists_OnClick(INT_PTR treeId, int clickType, HWND wnd);
int playlists_OnKeyDown(int treeId, NMTVKEYDOWN *p, HWND hwndDlg);
int playlists_OnDrag(int treeId, POINT *pt, int *type);
int playlists_OnDrop(int treeId, POINT *pt, int destTreeId);
int playlists_CloudAvailable();
int playlists_CloudInstalled();
/* SendTo.cpp */
int playlists_BuildSendTo(int sourceType, INT_PTR context);
void AddPlaylistFromFilenames(const char *filenames, const wchar_t *playlistTitle, int makeTree, const wchar_t *filename=0);
void AddPlaylistFromFilenamesW(const wchar_t *filenames, const wchar_t *playlistTitle, int makeTree, const wchar_t *filename=0);
void AddPlaylistFromItemRecordList(itemRecordList *obj, const wchar_t *playlistTitle, int makeTree, const wchar_t *filename=0);
void AddPlaylistFromItemRecordListW(itemRecordListW *obj, const wchar_t *playlistTitle, int makeTree, const wchar_t *filename=0);
int playlists_OnDropTarget(int id, int type, INT_PTR data);
int playlists_OnSendTo(int sourceType, INT_PTR data, int id);
void SwapPlayEnqueueInMenu(HMENU listMenu);
void SyncMenuWithAccelerators(HWND hwndDlg, HMENU menu);
BOOL CALLBACK browseEnumProc(HWND hwnd, LPARAM lParam);
extern int uniqueAddress;
typedef std::map<INT_PTR, GUID> TREE_TO_GUID_MAP;
extern TREE_TO_GUID_MAP tree_to_guid_map;
extern viewButtons view;
#define DEFAULT_PL_SEND_TO 1
#endif

View File

@ -0,0 +1,237 @@
//#define PLUGIN_NAME "Nullsoft Playlists"
#define PLUGIN_VERSION L"1.78"
#include "main.h"
#include "../../General/gen_ml/ml_ipc_0313.h"
#include "api__ml_playlists.h"
#include "replicant/nu/AutoChar.h"
#include "PlaylistsCOM.h"
#include "api/service/waservicefactory.h"
#include "PlaylistsCB.h"
#include "playlist/plstring.h"
#define PLAYLIST_IMAGE_INDEX 201
#define PLAYLIST_CLOUD_IMAGE_INDEX 202
PlaylistsCOM playlistsCOM;
HMENU wa_playlists_cmdmenu = NULL;
HMENU wa_play_menu = NULL;
INT_PTR playlistsTreeId = 0, playlistsCloudTreeId = 3002;
HNAVITEM playlistItem = 0;
wchar_t g_path[ MAX_PATH ] = { 0 };
HMENU g_context_menus = 0, g_context_menus2 = 0, g_context_menus3 = 0;
int Init();
void Quit();
int( *warand )( void ) = 0;
INT_PTR sendToIgnoreID = 0;
int IPC_LIBRARY_PLAYLISTS_REFRESH = -1, IPC_CLOUD_ENABLED = -1;
extern "C" winampMediaLibraryPlugin plugin =
{
MLHDR_VER,
"nullsoft(ml_playlists.dll)",
Init,
Quit,
pluginMessageProc,
0,
0,
0,
};
HCURSOR hDragNDropCursor;
C_Config *g_config = 0;
int imgPL = 0, imgCloudPL = 0;
api_playlistmanager *AGAVE_API_PLAYLISTMANAGER = 0;
api_application *WASABI_API_APP = 0;
api_playlists *AGAVE_API_PLAYLISTS = 0;
api_downloadManager *WAC_API_DOWNLOADMANAGER = 0;
api_syscb *WASABI_API_SYSCB = 0;
api_explorerfindfile *WASABI_API_EXPLORERFINDFILE = 0;
PlaylistsCB playlistsCB;
// wasabi based services for localisation support
api_language *WASABI_API_LNG = 0;
HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
template <class api_t>
api_t *GetService( GUID serviceGUID )
{
waServiceFactory *sf = plugin.service->service_getServiceByGuid( serviceGUID );
if ( sf )
return (api_t *)sf->getInterface();
else
return 0;
}
inline void ReleaseService( GUID serviceGUID, void *service )
{
waServiceFactory *sf = plugin.service->service_getServiceByGuid( serviceGUID );
if ( sf )
sf->releaseInterface( service );
}
wchar_t prefsname[ 64 ] = { 0 };
extern WORD waMenuID;
int Init()
{
waMenuID = (WORD)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_REGISTER_LOWORD_COMMAND );
AGAVE_API_PLAYLISTMANAGER = GetService<api_playlistmanager>( api_playlistmanagerGUID );
if ( !AGAVE_API_PLAYLISTMANAGER ) // no sense in continuing
return ML_INIT_FAILURE;
AGAVE_API_PLAYLISTS = GetService<api_playlists>( api_playlistsGUID );
if ( !AGAVE_API_PLAYLISTS ) // no sense in continuing
return ML_INIT_FAILURE;
WAC_API_DOWNLOADMANAGER = GetService<api_downloadManager>( DownloadManagerGUID );
WASABI_API_APP = GetService<api_application>( applicationApiServiceGuid );
WASABI_API_SYSCB = GetService<api_syscb>( syscbApiServiceGuid );
WASABI_API_SYSCB->syscb_registerCallback( &playlistsCB );
WASABI_API_EXPLORERFINDFILE = GetService<api_explorerfindfile>( ExplorerFindFileApiGUID );
// Hook(plugin.hwndWinampParent);
warand = ( int( * )( void ) )SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_RANDFUNC );
// need to get WASABI_API_APP first
plstring_init();
// loader so that we can get the localisation service api for use
WASABI_API_LNG = GetService<api_language>( languageApiGUID );
// need to have this initialised before we try to do anything with localisation features
WASABI_API_START_LANG( plugin.hDllInstance, MlPlaylistsLangGUID );
static wchar_t szDescription[ 256 ];
swprintf( szDescription, ARRAYSIZE( szDescription ), WASABI_API_LNGSTRINGW( IDS_NULLSOFT_PLAYLISTS ), PLUGIN_VERSION );
plugin.description = (char *)szDescription;
mediaLibrary.library = plugin.hwndLibraryParent;
mediaLibrary.winamp = plugin.hwndWinampParent;
mediaLibrary.instance = plugin.hDllInstance;
mediaLibrary.GetWinampIni(); // to prevent a SendMessage hang later
mediaLibrary.AddDispatch( L"Playlists", &playlistsCOM );
wchar_t inifile[ MAX_PATH ] = { 0 };
mediaLibrary.BuildPath( L"Plugins", inifile, MAX_PATH );
CreateDirectoryW( inifile, NULL );
mediaLibrary.BuildPath( L"Plugins\\gen_ml.ini", inifile, MAX_PATH );
g_config = new C_Config( inifile );
enqueuedef = g_config->ReadInt( L"enqueuedef", 0 );
// if m3udir has been changed (not the same as inidir) then
// we attempt to use the same folder for our playlist files
const wchar_t *m3udir = (wchar_t *)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETM3UDIRECTORYW );
const wchar_t *inidir = mediaLibrary.GetIniDirectoryW();
if ( !lstrcmpiW( m3udir, inidir ) )
mediaLibrary.BuildPath( L"Plugins\\ml\\playlists", g_path, MAX_PATH );
else
lstrcpynW( g_path, m3udir, MAX_PATH );
CreateDirectoryW( g_path, NULL );
g_context_menus = WASABI_API_LOADMENU( IDR_MENU1 );
g_context_menus2 = WASABI_API_LOADMENU( IDR_MENU1 ); // this and next one are used for the combined buttons
g_context_menus3 = WASABI_API_LOADMENU( IDR_MENU1 ); // so we don't have to mess around the translators etc
HookMediaLibrary();
hDragNDropCursor = LoadCursor( GetModuleHandle( L"gen_ml.dll" ), MAKEINTRESOURCE( ML_IDC_DRAGDROP ) );
HMENU wa_plcontext_menu = GetSubMenu( (HMENU)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, -1, IPC_GET_HMENU ), 2 );
if ( wa_plcontext_menu )
{
wa_playlists_cmdmenu = GetSubMenu( wa_plcontext_menu, 4 );
if ( wa_playlists_cmdmenu )
{
MENUITEMINFO i = { sizeof( i ), MIIM_TYPE, MFT_SEPARATOR, 0, 0 };
InsertMenuItem( wa_playlists_cmdmenu, 9, TRUE, &i );
MENUITEMINFO j = { sizeof( i ), MIIM_ID | MIIM_STATE | MIIM_TYPE, MFT_STRING, 0, WINAMP_MANAGEPLAYLISTS };
j.dwTypeData = WASABI_API_LNGSTRINGW( IDS_MANAGE_PLAYLISTS );
InsertMenuItem( wa_playlists_cmdmenu, 10, TRUE, &j );
}
}
IPC_CLOUD_ENABLED = SendMessage( plugin.hwndWinampParent, WM_WA_IPC, ( WPARAM ) & "WinampCloudEnabled", IPC_REGISTER_WINAMP_IPCMESSAGE );
IPC_LIBRARY_PLAYLISTS_REFRESH = SendMessage( plugin.hwndWinampParent, WM_WA_IPC, ( WPARAM ) & "ml_playlist_refresh", IPC_REGISTER_WINAMP_IPCMESSAGE );
wa_play_menu = GetSubMenu( (HMENU)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_HMENU ), 2 );
// lets extend menu that called on button press
int IPC_GET_ML_HMENU = (int)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, ( WPARAM ) & "LibraryGetHmenu", IPC_REGISTER_WINAMP_IPCMESSAGE );
HMENU context_menu = (HMENU)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_ML_HMENU );
if ( context_menu )
{
HMENU btnMenu = GetSubMenu( context_menu, 0 );
if ( btnMenu )
{
MENUITEMINFO mii = { sizeof( MENUITEMINFO ) };
mii.fMask = MIIM_FTYPE;
mii.fType = MFT_SEPARATOR;
mii.fState = MFS_ENABLED;
InsertMenuItem( btnMenu, 0, TRUE, &mii );
mii.fMask = MIIM_TYPE | MIIM_ID;
mii.fType = MFT_STRING;
mii.dwTypeData = WASABI_API_LNGSTRINGW( IDS_MANAGE_PLAYLISTS );
mii.cch = (unsigned int)lstrlen( mii.dwTypeData );
mii.wID = WINAMP_MANAGEPLAYLISTS;
InsertMenuItem( btnMenu, 0, TRUE, &mii );
mii.dwTypeData = WASABI_API_LNGSTRINGW( IDS_NEW_PLAYLIST );
mii.cch = (unsigned int)lstrlen( mii.dwTypeData );
mii.wID = ID_DOSHITMENU_ADDNEWPLAYLIST;
InsertMenuItem( btnMenu, 0, TRUE, &mii );
}
}
imgPL = mediaLibrary.AddTreeImage( IDB_TREEITEM_PLAYLIST, PLAYLIST_IMAGE_INDEX, (BMPFILTERPROC)FILTER_DEFAULT1 );
imgCloudPL = mediaLibrary.AddTreeImage( IDB_TREEITEM_CLOUD_PLAYLIST, PLAYLIST_CLOUD_IMAGE_INDEX, (BMPFILTERPROC)FILTER_DEFAULT1 );
NAVINSERTSTRUCT nis = { 0 };
nis.item.cbSize = sizeof( NAVITEM );
nis.item.pszText = WASABI_API_LNGSTRINGW_BUF( IDS_PLAYLISTS, prefsname, 64 );
nis.item.pszInvariant = L"Playlists";
nis.item.id = playlistsTreeId = 3001; // for backwards compatability
nis.item.style = NIS_HASCHILDREN;
nis.item.mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_ITEMID | NIMF_STYLE;
playlistItem = MLNavCtrl_InsertItem( plugin.hwndLibraryParent, &nis );
LoadPlaylists();
Hook( plugin.hwndWinampParent );
HookPlaylistEditor();
return ML_INIT_SUCCESS;
}
void Quit()
{
AGAVE_API_PLAYLISTS->Flush();
WASABI_API_SYSCB->syscb_deregisterCallback( &playlistsCB );
ReleaseService( api_playlistmanagerGUID, AGAVE_API_PLAYLISTMANAGER );
ReleaseService( api_playlistsGUID, AGAVE_API_PLAYLISTS );
ReleaseService( DownloadManagerGUID, WAC_API_DOWNLOADMANAGER );
ReleaseService( applicationApiServiceGuid, WASABI_API_APP );
ReleaseService( syscbApiServiceGuid, WASABI_API_SYSCB );
ReleaseService( ExplorerFindFileApiGUID, WASABI_API_EXPLORERFINDFILE );
UnhookPlaylistEditor();
UnhookMediaLibrary();
delete g_config;
}
extern "C" __declspec( dllexport ) winampMediaLibraryPlugin * winampGetMediaLibraryPlugin()
{
return &plugin;
}

View File

@ -0,0 +1,498 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"#include ""version.rc2""\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_BROWSE_PLFLD DIALOGEX 0, 0, 254, 29
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
CONTROL "Use original playlist instead of a Winamp managed copy", IDC_EXTERNAL,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP, 5, 4, 193, 10
CONTROL "Recursive scan", IDC_CHECK1, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, 5, 17, 64, 10
CONTROL "Available in the Cloud", IDC_CLOUD, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, 83, 17, 85, 10
END
IDD_VIEW_PLAYLISTS DIALOGEX 0, 0, 184, 266
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU
EXSTYLE WS_EX_ACCEPTFILES
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "", IDC_PLAYLIST_LIST, "SysListView32", LVS_REPORT | LVS_SHOWSELALWAYS | LVS_OWNERDATA | LVS_NOSORTHEADER | WS_TABSTOP, 0, 0, 182, 253
CONTROL "View Playlist", IDC_VIEWLIST, "Button", BS_OWNERDRAW | WS_TABSTOP, 0, 255, 37, 11
CONTROL "Play", IDC_PLAY, "Button", BS_OWNERDRAW | WS_TABSTOP, 39, 255, 22, 11
CONTROL "Enqueue", IDC_ENQUEUE, "Button", BS_OWNERDRAW | WS_TABSTOP, 63, 255, 36, 11
CONTROL "New...", IDC_CREATENEWPL, "Button", BS_OWNERDRAW | WS_TABSTOP, 101, 255, 22, 11
CONTROL "Import", IDC_IMPORT, "Button", BS_OWNERDRAW | WS_TABSTOP, 125, 255, 29, 11
CONTROL "Save...", IDC_SAVE, "Button", BS_OWNERDRAW | WS_TABSTOP, 156, 255, 28, 11
END
IDD_RENAME_PLAYLIST DIALOGEX 0, 0, 186, 42
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Rename Playlist"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Name:", IDC_STATIC, 5, 9, 22, 8
EDITTEXT IDC_NAME, 35, 7, 145, 12, ES_AUTOHSCROLL
DEFPUSHBUTTON "OK", IDOK, 76, 25, 50, 13
PUSHBUTTON "Cancel", IDCANCEL, 130, 25, 50, 13
END
IDD_ADD_PLAYLIST DIALOGEX 0, 0, 186, 42
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "New Playlist"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Name:", IDC_STATIC, 5, 9, 22, 8
EDITTEXT IDC_NAME, 35, 7, 145, 12, ES_AUTOHSCROLL
DEFPUSHBUTTON "OK", IDOK, 76, 25, 50, 13
PUSHBUTTON "Cancel", IDCANCEL, 130, 25, 50, 13
END
IDD_VIEW_PLAYLIST DIALOGEX 0, 0, 339, 59
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU
EXSTYLE WS_EX_ACCEPTFILES
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "", IDC_PLAYLIST_EDITOR, "SysListView32", LVS_REPORT | LVS_SHOWSELALWAYS | LVS_OWNERDATA | LVS_NOSORTHEADER | WS_TABSTOP, 0, 0, 339, 34
LTEXT "", IDC_PLSTATUS, 0, 36, 339, 10, SS_CENTERIMAGE | SS_ENDELLIPSIS
CONTROL "Play", IDC_PLAY, "Button", BS_OWNERDRAW | WS_TABSTOP, 0, 48, 27, 11
CONTROL "Send-To", IDC_BURN, "Button", BS_OWNERDRAW | WS_TABSTOP, 34, 48, 44, 11
CONTROL "Add", IDC_ADD, "Button", BS_OWNERDRAW | WS_TABSTOP, 85, 48, 30, 11
CONTROL "Rem", IDC_REM, "Button", BS_OWNERDRAW | WS_TABSTOP, 122, 48, 30, 11
CONTROL "Sel", IDC_SEL, "Button", BS_OWNERDRAW | WS_TABSTOP, 159, 48, 30, 11
CONTROL "Misc", IDC_MISC, "Button", BS_OWNERDRAW | WS_TABSTOP, 196, 48, 30, 11
CONTROL "Manage Playlist", IDC_LIST, "Button", BS_OWNERDRAW | WS_TABSTOP, 233, 48, 69, 11
CONTROL "Save", IDC_SAVE_PL, "Button", BS_OWNERDRAW | WS_DISABLED | WS_TABSTOP, 309, 48, 30, 11
END
IDD_EDIT_FN DIALOGEX 0, 0, 262, 95
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Edit Playlist Entry"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
RTEXT "Old Entry:", IDC_STATIC, 4, 7, 40, 12, SS_CENTERIMAGE
EDITTEXT IDC_OLD, 49, 7, 206, 12, ES_AUTOHSCROLL | ES_READONLY | WS_DISABLED
RTEXT "Old Title:", IDC_STATIC, 4, 24, 40, 12, SS_CENTERIMAGE
EDITTEXT IDC_OLD_TITLE, 49, 24, 206, 12, ES_AUTOHSCROLL | ES_READONLY | WS_DISABLED
RTEXT "New Entry:", IDC_STATIC, 4, 41, 40, 12, SS_CENTERIMAGE
EDITTEXT IDC_NEW, 49, 41, 188, 12, ES_AUTOHSCROLL
PUSHBUTTON "&...", IDC_PLAYLIST_EDIT_ENTRY_BROWSE, 238, 40, 17, 14
RTEXT "New Title:", IDC_STATIC, 4, 58, 40, 12, SS_CENTERIMAGE
EDITTEXT IDC_NEW_TITLE, 49, 58, 206, 12, ES_AUTOHSCROLL
DEFPUSHBUTTON "OK", IDOK, 5, 76, 50, 14
PUSHBUTTON "Cancel", IDCANCEL, 61, 76, 50, 14
END
IDD_SELECT_PLAYLIST DIALOGEX 0, 0, 209, 42
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Select Playlist"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
COMBOBOX IDC_PLAYLISTS, 4, 7, 200, 158, CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "Cancel", IDCANCEL, 154, 25, 50, 13
DEFPUSHBUTTON "OK", IDOK, 100, 25, 50, 13
END
#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
#if defined(APSTUDIO_INVOKED)
IDD_ADD_CLOUD_PLAYLIST$( DISABLED ) DIALOGEX 0, 0, 186, 42
#else
IDD_ADD_CLOUD_PLAYLIST DIALOGEX 0, 0, 186, 42
#endif
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "New Playlist"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Name:", IDC_STATIC, 5, 9, 22, 8
EDITTEXT IDC_NAME, 35, 7, 145, 12, ES_AUTOHSCROLL
CONTROL "&Available in the Cloud", IDC_CLOUD, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, 6, 26, 88, 11
DEFPUSHBUTTON "&OK", IDOK, 98, 25, 36, 13
PUSHBUTTON "&Cancel", IDCANCEL, 138, 25, 42, 13
END
#endif
IDD_IMPORT_PLFLD DIALOGEX 0, 0, 400, 20
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
CONTROL "Available in the Cloud", IDC_CLOUD, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, 67, 3, 85, 10
CONTROL "Use original playlist instead of a Winamp managed copy", IDC_EXTERNAL,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP, 167, 3, 193, 10
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_BROWSE_PLFLD, DIALOG
BEGIN
RIGHTMARGIN, 109
BOTTOMMARGIN, 18
END
IDD_RENAME_PLAYLIST, DIALOG
BEGIN
LEFTMARGIN, 5
RIGHTMARGIN, 180
TOPMARGIN, 7
BOTTOMMARGIN, 38
END
IDD_ADD_PLAYLIST, DIALOG
BEGIN
LEFTMARGIN, 5
RIGHTMARGIN, 180
TOPMARGIN, 7
BOTTOMMARGIN, 38
END
IDD_VIEW_PLAYLIST, DIALOG
BEGIN
RIGHTMARGIN, 295
END
IDD_EDIT_FN, DIALOG
BEGIN
LEFTMARGIN, 5
RIGHTMARGIN, 254
RIGHTMARGIN, 255
TOPMARGIN, 7
BOTTOMMARGIN, 90
END
IDD_SELECT_PLAYLIST, DIALOG
BEGIN
LEFTMARGIN, 4
RIGHTMARGIN, 204
TOPMARGIN, 7
BOTTOMMARGIN, 38
END
"IDD_ADD_CLOUD_PLAYLIST$(DISABLED)", DIALOG
BEGIN
LEFTMARGIN, 5
RIGHTMARGIN, 180
TOPMARGIN, 7
BOTTOMMARGIN, 38
END
IDD_IMPORT_PLFLD, DIALOG
BEGIN
RIGHTMARGIN, 227
BOTTOMMARGIN, 15
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
IDR_MENU1 MENU
BEGIN
POPUP "Playlist"
BEGIN
MENUITEM "Play\tEnter", IDC_PLAY
MENUITEM "Enqueue\tShift+Enter", IDC_ENQUEUE
POPUP "Send to:"
BEGIN
MENUITEM "", 40003
END
MENUITEM SEPARATOR
MENUITEM "View Playlist\tCtrl+V", IDC_VIEWLIST
MENUITEM SEPARATOR
MENUITEM "Rename Playlist...\tF2", IDC_RENAME
MENUITEM "New Playlist...\tShift+Insert", IDC_NEWPLAYLIST
MENUITEM SEPARATOR
MENUITEM "Delete Playlist\tDel", IDC_DELETE
END
POPUP "Playlistsmenu"
BEGIN
MENUITEM "New Playlist...", IDC_NEWPLAYLIST
MENUITEM SEPARATOR
MENUITEM "Import playlist from file...", IDC_IMPORT_PLAYLIST_FROM_FILE
MENUITEM "Import active (current) playlist", IDC_IMPORT_WINAMP_PLAYLIST
MENUITEM "Import playlist from folder...", 40010
MENUITEM SEPARATOR
POPUP "Sort playlists by:"
BEGIN
MENUITEM "Title alphabetically (A->Z)", ID_SORTPLAYLIST_TITLE_A_Z
MENUITEM "Title alphabetically (Z->A)", ID_SORTPLAYLIST_TITLE_Z_A
MENUITEM "Number of items ascending", ID_SORTPLAYLIST_NUMBEROFITEMSASCENDING
MENUITEM "Number of items descending", ID_SORTPLAYLIST_NUMBEROFITEMSDESCENDING
END
MENUITEM SEPARATOR
MENUITEM "Help", ID_PLAYLISTS_HELP
END
POPUP "PlaylistsImport"
BEGIN
MENUITEM "Import playlists from folder...", ID_PLAYLISTSIMPORT_IMPORTPLAYLISTSFROMFOLDER
MENUITEM "Import (active) current playlist", IDC_IMPORT_WINAMP_PLAYLIST
MENUITEM "Import playlist from file...", IDC_IMPORT_PLAYLIST_FROM_FILE
END
POPUP "PEditorMenus"
BEGIN
POPUP "RightClick"
BEGIN
MENUITEM "Play selection\tEnter", IDC_PLAY
MENUITEM "Enqueue selection\tShift+Enter", IDC_ENQUEUE
POPUP "Send to:"
BEGIN
MENUITEM "", 40003
END
MENUITEM SEPARATOR
MENUITEM "Remove item(s)\tDelete", IDC_DELETE
MENUITEM "Crop item(s)\tCtrl+Delete", IDC_CROP
MENUITEM SEPARATOR
MENUITEM "Download\t", IDC_PLAYLIST_DOWNLOAD_ENTRY
MENUITEM SEPARATOR
MENUITEM "View f&ile info...\tAlt+3", IDC_PLAYLIST_VIEW_FILE_INFO
MENUITEM "Playlist entry\tCtrl+E", IDC_PLAYLIST_EDIT_ENTRY
MENUITEM SEPARATOR
MENUITEM "Explore item folder\tCtrl+F", IDC_PLAYLIST_EXPLOREITEMFOLDER
END
POPUP "Selection"
BEGIN
MENUITEM "Select &all\tCtrl+A", IDC_PLAYLIST_SELECT_ALL
MENUITEM "Select &none", IDC_PLAYLIST_SELECT_NONE
MENUITEM "Invert selection\tCtrl+I", IDC_PLAYLIST_INVERT_SELECTION
END
POPUP "Remove"
BEGIN
MENUITEM "Remove all &dead files", IDC_PLAYLIST_REMOVE_DEAD
MENUITEM SEPARATOR
MENUITEM "Remove all items", IDC_PLAYLIST_REMOVE_ALL
MENUITEM "Crop selected items\tCtrl+Delete", IDC_CROP
MENUITEM "Remove selected items\tDelete", IDC_DELETE
MENUITEM "Physically remove selected item(s)", IDC_PLAYLIST_RECYCLE_SELECTED
END
POPUP "Add"
BEGIN
MENUITEM "Add file(s)\tL", IDC_ADD_FILES
MENUITEM "Add folder\tShift+L", IDC_ADD_DIRECTORY
MENUITEM "Add URL\tCtrl+L", IDC_ADD_LOCATION
END
POPUP "Misc"
BEGIN
MENUITEM "&Randomize list\tCtrl+Shift+R", IDC_PLAYLIST_RANDOMIZE
MENUITEM "R&everse list\tCtrl+R", IDC_PLAYLIST_REVERSE
MENUITEM SEPARATOR
MENUITEM "Sort list by &path and filename", IDC_PLAYLIST_SORT_PATH
MENUITEM "Sort list by &filename", IDC_PLAYLIST_SORT_FILENAME
MENUITEM "Sort list by &title", IDC_PLAYLIST_SORT_TITLE
MENUITEM SEPARATOR
MENUITEM "Generate HTML playlist\tCtrl+Alt+G", ID_PLAYLIST_GENERATE_HTML
MENUITEM SEPARATOR
MENUITEM "Rebuild titles on selection\tCtrl+Shift+E", IDC_PLAYLIST_RESET_CACHE
END
POPUP "List"
BEGIN
MENUITEM "Export playlist...", IDC_EXPORT_PLAYLIST
POPUP "Send playlist to"
BEGIN
MENUITEM "", 40003
END
MENUITEM SEPARATOR
MENUITEM "Import playlist from file...", IDC_IMPORT_PLAYLIST_FROM_FILE
MENUITEM "Import active playlist", IDC_IMPORT_WINAMP_PLAYLIST
END
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//
IDB_TREEITEM_PLAYLIST BITMAP "resources\\ti_playlist_16x16x16.bmp"
#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
#if defined(APSTUDIO_INVOKED)
IDB_TREEITEM_CLOUD_PLAYLIST$( DISABLED ) BITMAP "resources\\ti_cloud_playlist_16x16x16.bmp"
#else
IDB_TREEITEM_CLOUD_PLAYLIST BITMAP "resources\\ti_cloud_playlist_16x16x16.bmp"
#endif
#endif
/////////////////////////////////////////////////////////////////////////////
//
// Accelerator
//
IDR_VIEW_PLS_ACCELERATORS ACCELERATORS
BEGIN
VK_DELETE, IDC_DELETE, VIRTKEY, NOINVERT
VK_RETURN, IDC_ENQUEUE, VIRTKEY, SHIFT, NOINVERT
VK_INSERT, IDC_NEWPLAYLIST, VIRTKEY, SHIFT, NOINVERT
VK_RETURN, IDC_PLAY, VIRTKEY, NOINVERT
VK_F2, IDC_RENAME, VIRTKEY, NOINVERT
"V", IDC_VIEWLIST, VIRTKEY, CONTROL, NOINVERT
VK_RETURN, IDC_CUSTOM, VIRTKEY, SHIFT, CONTROL, NOINVERT
END
IDR_VIEW_PL_ACCELERATORS ACCELERATORS
BEGIN
"3", IDC_PLAYLIST_VIEW_FILE_INFO, VIRTKEY, ALT, NOINVERT
"A", IDC_PLAYLIST_SELECT_ALL, VIRTKEY, CONTROL, NOINVERT
"E", IDC_PLAYLIST_EDIT_ENTRY, VIRTKEY, CONTROL, NOINVERT
"E", IDC_PLAYLIST_RESET_CACHE, VIRTKEY, CONTROL, ALT, NOINVERT
"F", IDC_PLAYLIST_EXPLOREITEMFOLDER, VIRTKEY, CONTROL, NOINVERT
"G", ID_PLAYLIST_GENERATE_HTML, VIRTKEY, CONTROL, ALT, NOINVERT
"I", IDC_PLAYLIST_INVERT_SELECTION, VIRTKEY, CONTROL, NOINVERT
"L", IDC_ADD_FILES, VIRTKEY, NOINVERT
"L", IDC_ADD_LOCATION, VIRTKEY, CONTROL, NOINVERT
"L", IDC_ADD_DIRECTORY, VIRTKEY, SHIFT, NOINVERT
"R", IDC_PLAYLIST_REVERSE, VIRTKEY, CONTROL, NOINVERT
"R", IDC_PLAYLIST_RANDOMIZE, VIRTKEY, SHIFT, CONTROL, NOINVERT
VK_DELETE, IDC_DELETE, VIRTKEY, NOINVERT
VK_DELETE, IDC_CROP, VIRTKEY, CONTROL, NOINVERT
VK_F2, IDC_RENAME, VIRTKEY, NOINVERT
VK_INSERT, IDC_NEWPLAYLIST, VIRTKEY, SHIFT, NOINVERT
VK_RETURN, IDC_PLAY, VIRTKEY, NOINVERT
VK_RETURN, IDC_ENQUEUE, VIRTKEY, SHIFT, NOINVERT
VK_RETURN, IDC_CUSTOM, VIRTKEY, SHIFT, CONTROL, NOINVERT
END
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE
BEGIN
IDS_NULLSOFT_PLAYLISTS "Nullsoft Playlists v%s"
65535 "{5E766B4F-818E-4f14-9C42-0902B2C571DC}"
END
STRINGTABLE
BEGIN
IDS_IMPORT_PLAYLIST "Import playlist"
IDS_APPEND_IMPORTED_PLAYLIST
"Would you like to append the imported playlist to this playlist?\n\nSelect NO to overwrite this playlist with the imported playlist\nSelect YES to append the imported playlist to this playlist\nSelect CANCEL to not do anything"
IDS_LIBRARY_QUESTION "Library Question"
IDS_APPEND_ACTIVE_PLAYLIST
"Would you like to append the active playlist to this playlist?\n\nSelect NO to overwrite this playlist with the active playlist\nSelect YES to append the active playlist to this playlist\nSelect CANCEL to not do anything"
IDS_EXPORT_PLAYLIST "Export playlist"
IDS_IMPORTED_PLAYLIST "Imported Playlist"
IDS_ITEM "item"
IDS_ADD_DIR_TO_PLAYLIST "Add directory to playlist"
IDS_ADD_FILES_TO_PLAYLIST "Add file(s) to playlist"
IDS_PLAYLISTS "Playlists"
IDS_MANAGE_PLAYLISTS "&Manage Playlists..."
IDS_NEW_PLAYLIST "New Play&list...\tShift+Insert"
IDS_ENTER_A_NAME "You must enter a name!"
IDS_ERROR "Error"
END
STRINGTABLE
BEGIN
IDS_SENDTO_NEW_PLAYLIST "New Playlist"
IDS_SENDTO_ML_PLAYLISTS "Library Playlists"
IDS_SENDTO_PLAYLIST "Library Playlist"
IDS_IMPORT_PLAYLIST_FROM_FOLDER "Import playlist from folder"
IDS_CONFIRM_DELETION "Are you sure you want to delete the selected playlist(s)"
IDS_CONFIRMATION "Confirmation"
IDS_PLAYLIST_TITLE "Playlist Title"
IDS_ITEMS "Items"
IDS_TIME "Time"
IDS_ERROR_DELETING_FILES "Error deleting files"
IDS_X_OF_X_SELECTED "%d/%d %s selected (%s/%s)"
IDS_X_SELECTED "%d %s (%s)"
IDS_ITEMS_LOWER "items"
IDS_TITLE "Title"
IDS_NO_PLAYLIST_IN_LIBRARY "No Playlist in Library"
IDS_OPEN_PLAYLIST_FROM_ML "Open playlist from &Library"
END
STRINGTABLE
BEGIN
IDS_PLAYLIST_FROM_ML "Playlist from &Library"
IDS_PLAYLIST_ERROR "Cannot save playlist: Error writing to file or file is read-only"
IDS_PLAYLIST_ERROR_TITLE "Error saving playlist"
IDS_DAY "day"
IDS_DAYS "days"
IDS_PL_FILE_MNGT "Winamp Playlist Management"
IDS_EXTERNAL_CHECKED "Checking this will make Winamp use the original playlist file(s) instead of creating a Winamp managed copy which is not synchronized with the original playlist file(s).\n\nIf checked, you need to ensure the playlist file(s) exist and will be accessible always otherwise they will not be useable within Winamp. All actions will be done on the original playlist file e.g. delete will remove the original file instead of just removing the Winamp managed copy."
IDS_EXTERNAL_ALREADY_ADDED
"The playlist you have selected has already been imported into Winamp and will not be re-added."
IDS_SOURCE_PL_MISSING "\n\nThe source playlist file for this playlist view cannot be located.\nThis can happen if the playlist has been physically removed.\n\nThe missing source playlist file is:\n%s"
END
STRINGTABLE
BEGIN
IDS_BROWSE_FOR_PLEDIT_ENTRY "Browse for playlist entry..."
END
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
#include "version.rc2"
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@ -0,0 +1,81 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29509.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ml_playlists", "ml_playlists.vcxproj", "{71726F38-0625-4E16-BA79-32FE4F83E1E4}"
ProjectSection(ProjectDependencies) = postProject
{44AEBB50-1331-4F2E-8AEC-56C82DE16C11} = {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} = {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}
{E105A0A2-7391-47C5-86AC-718003524C3D} = {E105A0A2-7391-47C5-86AC-718003524C3D}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nx", "..\replicant\nx\nx.vcxproj", "{57C90706-B25D-4ACA-9B33-95CDB2427C27}"
ProjectSection(ProjectDependencies) = postProject
{44AEBB50-1331-4F2E-8AEC-56C82DE16C11} = {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "..\replicant\nu\nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jnetlib", "..\replicant\jnetlib\jnetlib.vcxproj", "{E105A0A2-7391-47C5-86AC-718003524C3D}"
ProjectSection(ProjectDependencies) = postProject
{44AEBB50-1331-4F2E-8AEC-56C82DE16C11} = {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\replicant\zlib\zlib.vcxproj", "{44AEBB50-1331-4F2E-8AEC-56C82DE16C11}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{71726F38-0625-4E16-BA79-32FE4F83E1E4}.Debug|Win32.ActiveCfg = Debug|Win32
{71726F38-0625-4E16-BA79-32FE4F83E1E4}.Debug|Win32.Build.0 = Debug|Win32
{71726F38-0625-4E16-BA79-32FE4F83E1E4}.Debug|x64.ActiveCfg = Debug|x64
{71726F38-0625-4E16-BA79-32FE4F83E1E4}.Debug|x64.Build.0 = Debug|x64
{71726F38-0625-4E16-BA79-32FE4F83E1E4}.Release|Win32.ActiveCfg = Release|Win32
{71726F38-0625-4E16-BA79-32FE4F83E1E4}.Release|Win32.Build.0 = Release|Win32
{71726F38-0625-4E16-BA79-32FE4F83E1E4}.Release|x64.ActiveCfg = Release|x64
{71726F38-0625-4E16-BA79-32FE4F83E1E4}.Release|x64.Build.0 = Release|x64
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.ActiveCfg = Debug|Win32
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.Build.0 = Debug|Win32
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.ActiveCfg = Debug|x64
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.Build.0 = Debug|x64
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.ActiveCfg = Release|Win32
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.Build.0 = Release|Win32
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.ActiveCfg = Release|x64
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.Build.0 = Release|x64
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.Build.0 = Debug|x64
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64
{E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.ActiveCfg = Debug|Win32
{E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.Build.0 = Debug|Win32
{E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.ActiveCfg = Debug|x64
{E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.Build.0 = Debug|x64
{E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.ActiveCfg = Release|Win32
{E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.Build.0 = Release|Win32
{E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.ActiveCfg = Release|x64
{E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.Build.0 = Release|x64
{44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Debug|Win32.ActiveCfg = Debug|Win32
{44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Debug|Win32.Build.0 = Debug|Win32
{44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Debug|x64.ActiveCfg = Debug|x64
{44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Debug|x64.Build.0 = Debug|x64
{44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Release|Win32.ActiveCfg = Release|Win32
{44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Release|Win32.Build.0 = Release|Win32
{44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Release|x64.ActiveCfg = Release|x64
{44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3815C7C8-CBE1-4F34-B885-16D73AD02A60}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,359 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{71726F38-0625-4E16-BA79-32FE4F83E1E4}</ProjectGuid>
<RootNamespace>ml_playlists</RootNamespace>
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
<IncludePath>$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
<EmbedManifest>true</EmbedManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
<EmbedManifest>true</EmbedManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
<IncludePath>$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
<EmbedManifest>true</EmbedManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
<EmbedManifest>true</EmbedManifest>
</PropertyGroup>
<PropertyGroup Label="Vcpkg">
<VcpkgEnableManifest>false</VcpkgEnableManifest>
</PropertyGroup>
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<VcpkgInstalledDir>
</VcpkgInstalledDir>
<VcpkgUseStatic>false</VcpkgUseStatic>
<VcpkgConfiguration>Debug</VcpkgConfiguration>
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
</PropertyGroup>
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<VcpkgInstalledDir>
</VcpkgInstalledDir>
<VcpkgUseStatic>false</VcpkgUseStatic>
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
</PropertyGroup>
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<VcpkgInstalledDir>
</VcpkgInstalledDir>
<VcpkgUseStatic>false</VcpkgUseStatic>
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
<VcpkgConfiguration>Debug</VcpkgConfiguration>
</PropertyGroup>
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<VcpkgInstalledDir>
</VcpkgInstalledDir>
<VcpkgUseStatic>false</VcpkgUseStatic>
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>.;..\..\..\Wasabi;..\..\..\dlmgr;..;..\..\..\;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_USRDLL;ML_PLAYLISTS_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4995;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<BufferSecurityCheck>true</BufferSecurityCheck>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Link>
<AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
<SubSystem>Windows</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX86</TargetMachine>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<AdditionalLibraryDirectories>..\ml_downloads\$(PlatformShortName)_$(Configuration)\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<PostBuildEvent>
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
</PostBuildEvent>
<ResourceCompile>
<PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<Manifest>
<OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
</Manifest>
<ProjectReference>
<UseLibraryDependencyInputs>false</UseLibraryDependencyInputs>
</ProjectReference>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>.;..\..\..\Wasabi;..;..\..\..\;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_WINDOWS;_USRDLL;ML_PLAYLISTS_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4244;4267;4838;4477;4090;4995;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<BufferSecurityCheck>true</BufferSecurityCheck>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Link>
<AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
<SubSystem>Windows</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<AdditionalLibraryDirectories>..\ml_downloads\$(PlatformShortName)_$(Configuration)\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<PostBuildEvent>
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
</PostBuildEvent>
<ResourceCompile>
<PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<Manifest>
<OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
</Manifest>
<ProjectReference>
<UseLibraryDependencyInputs>false</UseLibraryDependencyInputs>
</ProjectReference>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MinSpace</Optimization>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<AdditionalIncludeDirectories>.;..\..\..\Wasabi;..\..\..\dlmgr;..;..\..\..\;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_USRDLL;ML_PLAYLISTS_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<BufferSecurityCheck>true</BufferSecurityCheck>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>None</DebugInformationFormat>
<DisableSpecificWarnings>4995;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Link>
<AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<GenerateDebugInformation>false</GenerateDebugInformation>
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX86</TargetMachine>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
<PostBuildEvent>
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
</PostBuildEvent>
<ResourceCompile>
<PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<Manifest>
<OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
</Manifest>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<Optimization>MinSpace</Optimization>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<AdditionalIncludeDirectories>.;..\..\..\Wasabi;..;..\..\..\;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_WINDOWS;_USRDLL;ML_PLAYLISTS_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<BufferSecurityCheck>true</BufferSecurityCheck>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>None</DebugInformationFormat>
<DisableSpecificWarnings>4244;4267;4838;4477;4090;4995;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Link>
<AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<GenerateDebugInformation>false</GenerateDebugInformation>
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
<PostBuildEvent>
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
</PostBuildEvent>
<ResourceCompile>
<PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<Manifest>
<OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
</Manifest>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\nde\nde.vcxproj">
<Project>{4d25c321-7f8b-424e-9899-d80a364baf1a}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\replicant\nx\nx.vcxproj">
<Project>{57c90706-b25d-4aca-9b33-95cdb2427c27}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
<Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\WAT\WAT.vcxproj">
<Project>{c5714908-a71f-4644-bd95-aad8ee7914da}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\General\gen_ml\config.h" />
<ClInclude Include="..\..\General\gen_ml\menu.h" />
<ClInclude Include="..\..\..\nu\DialogSkinner.h" />
<ClInclude Include="..\..\..\playlist\plstring.h" />
<ClInclude Include="..\..\..\playlist\pl_entry.h" />
<ClInclude Include="..\..\..\Winamp\strutil.h" />
<ClInclude Include="api__ml_playlists.h" />
<ClInclude Include="CurrentPlaylist.h" />
<ClInclude Include="main.h" />
<ClInclude Include="Playlist.h" />
<ClInclude Include="PlaylistDirectoryCallback.h" />
<ClInclude Include="PlaylistInfo.h" />
<ClInclude Include="playlists.h" />
<ClInclude Include="PlaylistsCB.h" />
<ClInclude Include="PlaylistsCOM.h" />
<ClInclude Include="PlaylistView.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="SendTo.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\General\gen_ml\config.cpp" />
<ClCompile Include="..\..\General\gen_ml\menu.cpp" />
<ClCompile Include="..\..\General\gen_ml\ml_lib.cpp" />
<ClCompile Include="..\..\..\nu\DialogSkinner.cpp" />
<ClCompile Include="..\..\..\nu\listview.cpp" />
<ClCompile Include="..\..\..\nu\MediaLibraryInterface.cpp" />
<ClCompile Include="..\..\..\nu\menushortcuts.cpp" />
<ClCompile Include="..\..\..\playlist\plstring.cpp" />
<ClCompile Include="..\..\..\playlist\pl_entry.cpp" />
<ClCompile Include="..\..\..\Winamp\strutil.cpp" />
<ClCompile Include="AddPlaylist.cpp" />
<ClCompile Include="CurrentPlaylist.cpp" />
<ClCompile Include="ml_playlists.cpp" />
<ClCompile Include="ml_subclass.cpp" />
<ClCompile Include="pe_subclass.cpp" />
<ClCompile Include="Playlist.cpp" />
<ClCompile Include="PlaylistDirectoryCallback.cpp" />
<ClCompile Include="PlaylistInfo.cpp" />
<ClCompile Include="playlists.cpp" />
<ClCompile Include="PlaylistsCB.cpp" />
<ClCompile Include="PlaylistsCOM.cpp" />
<ClCompile Include="PlaylistView.cpp" />
<ClCompile Include="pluginproc.cpp" />
<ClCompile Include="RenamePlaylist.cpp" />
<ClCompile Include="SendTo.cpp" />
<ClCompile Include="view_pl.cpp" />
<ClCompile Include="view_playlists.cpp" />
<ClCompile Include="wa_subclass.cpp" />
</ItemGroup>
<ItemGroup>
<Text Include="Design.txt" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ml_playlists.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="resources\ti_cloud_playlist_16x16x16.bmp" />
<Image Include="resources\ti_playlist_16x16x16.bmp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,199 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="AddPlaylist.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CurrentPlaylist.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ml_playlists.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ml_subclass.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pe_subclass.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Playlist.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PlaylistDirectoryCallback.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PlaylistInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="playlists.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PlaylistsCB.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PlaylistsCOM.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PlaylistView.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="wa_subclass.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="view_playlists.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="view_pl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SendTo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RenamePlaylist.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pluginproc.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\General\gen_ml\config.cpp">
<Filter>Source Files\gen_ml</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nu\DialogSkinner.cpp">
<Filter>Source Files\nu</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nu\listview.cpp">
<Filter>Source Files\nu</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nu\MediaLibraryInterface.cpp">
<Filter>Source Files\nu</Filter>
</ClCompile>
<ClCompile Include="..\..\General\gen_ml\menu.cpp">
<Filter>Source Files\gen_ml</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nu\menushortcuts.cpp">
<Filter>Source Files\nu</Filter>
</ClCompile>
<ClCompile Include="..\..\General\gen_ml\ml_lib.cpp">
<Filter>Source Files\gen_ml</Filter>
</ClCompile>
<ClCompile Include="..\..\..\playlist\pl_entry.cpp">
<Filter>Source Files\playlist</Filter>
</ClCompile>
<ClCompile Include="..\..\..\playlist\plstring.cpp">
<Filter>Source Files\playlist</Filter>
</ClCompile>
<ClCompile Include="..\..\..\Winamp\strutil.cpp">
<Filter>Source Files\Winamp</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="api__ml_playlists.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CurrentPlaylist.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="main.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Playlist.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PlaylistDirectoryCallback.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PlaylistInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="playlists.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PlaylistsCB.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PlaylistsCOM.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PlaylistView.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SendTo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\General\gen_ml\config.h">
<Filter>Header Files\gen_ml</Filter>
</ClInclude>
<ClInclude Include="..\..\..\nu\DialogSkinner.h">
<Filter>Header Files\nu</Filter>
</ClInclude>
<ClInclude Include="..\..\General\gen_ml\menu.h">
<Filter>Header Files\gen_ml</Filter>
</ClInclude>
<ClInclude Include="..\..\..\playlist\pl_entry.h">
<Filter>Header Files\playlist</Filter>
</ClInclude>
<ClInclude Include="..\..\..\playlist\plstring.h">
<Filter>Header Files\playlist</Filter>
</ClInclude>
<ClInclude Include="..\..\..\Winamp\strutil.h">
<Filter>Header Files\Winamp</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="resources\ti_cloud_playlist_16x16x16.bmp">
<Filter>Image Files</Filter>
</Image>
<Image Include="resources\ti_playlist_16x16x16.bmp">
<Filter>Image Files</Filter>
</Image>
</ItemGroup>
<ItemGroup>
<Text Include="Design.txt" />
</ItemGroup>
<ItemGroup>
<Filter Include="Header Files">
<UniqueIdentifier>{510f3e24-3519-4357-a412-31ca9ea4e679}</UniqueIdentifier>
</Filter>
<Filter Include="Ressource Files">
<UniqueIdentifier>{bd3dfff8-2c37-43e7-8cb2-1d701dcaadaa}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{d90e2a08-7da5-425c-97e7-eb41aa99ab8f}</UniqueIdentifier>
</Filter>
<Filter Include="Image Files">
<UniqueIdentifier>{b45d999a-7960-4fef-b55a-90b9530879f2}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\gen_ml">
<UniqueIdentifier>{f4f89e40-7fa7-4faa-bdc9-bd6428e5132c}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\nu">
<UniqueIdentifier>{0ee115a9-708c-4311-859e-2aee4ffb1971}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\playlist">
<UniqueIdentifier>{2be30a31-bc6a-4220-9c58-832fd397d80a}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Winamp">
<UniqueIdentifier>{dd4f54cd-8631-41f9-92a8-1b4c37afa4ce}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\gen_ml">
<UniqueIdentifier>{21b175b4-e364-44c4-96f0-e67663b09ada}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\nu">
<UniqueIdentifier>{973fb9f8-4c13-41dd-aa80-c87c5664b559}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\playlist">
<UniqueIdentifier>{fd06d71f-c851-433d-a4bb-6458c5452422}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Winamp">
<UniqueIdentifier>{0df480a4-437b-429d-860f-7110e7d6c78d}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ml_playlists.rc">
<Filter>Ressource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,271 @@
#include <shlwapi.h>
#include "main.h"
#include "../winamp/wa_ipc.h"
#include "CurrentPlaylist.h"
#include "SendTo.h"
#include "Playlist.h"
#include "api__ml_playlists.h"
using namespace Nullsoft::Utility;
WNDPROC ml_wndProc = 0;
static INT_PTR CALLBACK AddPlaylistDialogProc_sc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static wchar_t *title;
switch (uMsg)
{
case WM_INITDIALOG:
title = (wchar_t*)lParam;
PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_NAME), TRUE);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
{
wchar_t name[256] = {0};
GetDlgItemText(hwndDlg, IDC_NAME, name, 255);
name[255] = 0;
if (!name[0])
{
wchar_t titleStr[32] = {0};
MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_ENTER_A_NAME),
WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,titleStr,32), MB_OK);
break;
}
lstrcpyn(title,name,256);
EndDialog(hwndDlg,1);
}
break;
case IDCANCEL:
EndDialog(hwndDlg,0);
break;
}
break;
}
return 0;
}
static void AddPlaylist(mlAddPlaylist *addPlaylist)
{
PlaylistInfo pl;
wchar_t filename[1024+256] = {0}; // use a longer buffer than MAX_PATH here because createPlayListDBFileName needs it
if (addPlaylist->flags & PL_FLAGS_IMPORT)
{
createPlayListDBFileName(filename);
CopyFileW(addPlaylist->filename, filename, FALSE);
}
else
lstrcpynW(filename, addPlaylist->filename, MAX_PATH);
int numItems = 0;
int length = 0;
wchar_t title[256] = {0};
if(addPlaylist->playlistName)
lstrcpynW(title, addPlaylist->playlistName, 256);
else // prompt for name
{
if(WASABI_API_DIALOGBOXPARAMW((playlists_CloudAvailable() ? IDD_ADD_CLOUD_PLAYLIST : IDD_ADD_PLAYLIST),
plugin.hwndLibraryParent, AddPlaylistDialogProc_sc, (LPARAM)title) == 0)
{ // the user hit cancel
return;
}
}
if (addPlaylist->numItems == -1 || addPlaylist->length == -1)
{
Playlist temp;
AGAVE_API_PLAYLISTMANAGER->Load(filename, &temp);
numItems = temp.GetNumItems();
for (size_t i = 0;i != numItems;i++)
{
int len = temp.GetItemLengthMilliseconds(i) / 1000;
if (len>=0)
length += len;
}
}
else
{
length = addPlaylist->length;
numItems = addPlaylist->numItems;
}
AddPlaylist(true, title, filename, !!(addPlaylist->flags & PL_FLAG_SHOW), g_config->ReadInt(L"cloud", 1), numItems, length);
}
static void MakePlaylist(mlMakePlaylistV2 *makePlaylist)
{
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
switch (makePlaylist->type)
{
case ML_TYPE_FILENAMES:
case ML_TYPE_STREAMNAMES:
AddPlaylistFromFilenames((const char *)makePlaylist->data, makePlaylist->playlistName, makePlaylist->flags);
if (makePlaylist->flags & PL_FLAG_FILL_FILENAME && makePlaylist->size == sizeof(mlMakePlaylistV2))
{
// TODO: not guaranteed to be at the end
size_t last_index = AGAVE_API_PLAYLISTS->GetCount() - 1;
lstrcpynW(makePlaylist->filename, AGAVE_API_PLAYLISTS->GetFilename(last_index), MAX_PATH);
}
return ;
case ML_TYPE_FILENAMESW:
case ML_TYPE_STREAMNAMESW:
AddPlaylistFromFilenamesW((const wchar_t *)makePlaylist->data, makePlaylist->playlistName, makePlaylist->flags);
if (makePlaylist->flags & PL_FLAG_FILL_FILENAME && makePlaylist->size == sizeof(mlMakePlaylistV2))
{
// TODO: not guaranteed to be at the end
size_t last_index = AGAVE_API_PLAYLISTS->GetCount() - 1;
lstrcpynW(makePlaylist->filename, AGAVE_API_PLAYLISTS->GetFilename(last_index), MAX_PATH);
}
return ;
case ML_TYPE_ITEMRECORDLIST:
case ML_TYPE_CDTRACKS:
AddPlaylistFromItemRecordList((itemRecordList *)makePlaylist->data, makePlaylist->playlistName, makePlaylist->flags);
if (makePlaylist->flags & PL_FLAG_FILL_FILENAME && makePlaylist->size == sizeof(mlMakePlaylistV2))
{
// TODO: not guaranteed to be at the end
size_t last_index = AGAVE_API_PLAYLISTS->GetCount() - 1;
lstrcpynW(makePlaylist->filename, AGAVE_API_PLAYLISTS->GetFilename(last_index), MAX_PATH);
}
return ;
case ML_TYPE_ITEMRECORDLISTW:
AddPlaylistFromItemRecordListW((itemRecordListW *)makePlaylist->data, makePlaylist->playlistName, makePlaylist->flags);
if (makePlaylist->flags & PL_FLAG_FILL_FILENAME && makePlaylist->size == sizeof(mlMakePlaylistV2))
{
// TODO: not guaranteed to be at the end
size_t last_index = AGAVE_API_PLAYLISTS->GetCount() - 1;
lstrcpynW(makePlaylist->filename, AGAVE_API_PLAYLISTS->GetFilename(last_index), MAX_PATH);
}
return ;
}
}
static int GetPlaylistInfo(mlPlaylistInfo *info)
{
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
size_t num = info->playlistNum;
if (num >= AGAVE_API_PLAYLISTS->GetCount())
return 0;
PlaylistInfo playlist(num);
lstrcpynW(info->filename, playlist.GetFilename(), MAX_PATH);
lstrcpynW(info->playlistName, playlist.GetName(), 128);
info->length = playlist.GetLength();
info->numItems = playlist.GetSize();
return 1;
}
static INT_PTR PlaylistIPC(int msg, INT_PTR param)
{
switch (msg)
{
case ML_IPC_NEWPLAYLIST: playlists_Add((HWND)param); return 1;
case ML_IPC_IMPORTPLAYLIST: Playlist_importFromFile((HWND)param); return 1;
case ML_IPC_SAVEPLAYLIST: CurrentPlaylist_Export((HWND)param); return 1; // TODO: can we guarantee a currently active playlist?
case ML_IPC_IMPORTCURRENTPLAYLIST: Playlist_importFromWinamp(); return 1;
// play/load the playlist passed as param
case ML_IPC_PLAY_PLAYLIST: PlayPlaylist(param); return 1;
case ML_IPC_LOAD_PLAYLIST: LoadPlaylist(param); return 1;
case ML_IPC_GETPLAYLISTWND: return(INT_PTR)activeHWND;
case ML_IPC_PLAYLIST_ADD: AddPlaylist((mlAddPlaylist *)param); return 1;
case ML_IPC_PLAYLIST_MAKE: MakePlaylist((mlMakePlaylistV2 *)param); return 1;
case ML_IPC_PLAYLIST_COUNT: return AGAVE_API_PLAYLISTS->GetCount();
case ML_IPC_PLAYLIST_INFO: return GetPlaylistInfo((mlPlaylistInfo *)param);
}
return 0;
}
extern SendToMenu treeViewSendTo;
INT_PTR CALLBACK MediaLibraryProcedure(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITMENUPOPUP:
if (treeViewSendTo.InitPopupMenu(wParam))
return 0;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case WINAMP_MANAGEPLAYLISTS:
mediaLibrary.SelectTreeItem(playlistsTreeId);
return 1;
case ID_DOSHITMENU_ADDNEWPLAYLIST:
playlists_Add(hwndDlg);
return 1;
}
}
break;
case WM_ML_IPC:
{
INT_PTR res = PlaylistIPC(lParam, wParam);
if (res)
{
SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, res);
return TRUE;
}
}
}
return CallWindowProc(ml_wndProc, hwndDlg, uMsg, wParam, lParam);
}
void HookMediaLibrary()
{
ml_wndProc = (WNDPROC)SetWindowLongPtr(plugin.hwndLibraryParent, DWLP_DLGPROC, (LONG_PTR)MediaLibraryProcedure);
}
void UnhookMediaLibrary()
{
SetWindowLongPtr(plugin.hwndLibraryParent, DWLP_DLGPROC, (LONG_PTR)ml_wndProc);
}
#define TREE_PLAYLIST_ID_START 3002
INT_PTR LoadPlaylist(INT_PTR treeId)
{
if (!FindTreeItem(treeId))
return 0;
wchar_t wstr[MAX_PATH+1] = {0};
{ // scope for lock
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
PlaylistInfo info;
info.Associate(treeId);
playlist_SaveGUID(info.playlist_guid);
memset(wstr, 0, sizeof(wstr)); // easy (but slow) double null terminate
PathCombineW(wstr, g_path, info.GetFilename());
}
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_DELETE);
enqueueFileWithMetaStructW s;
s.filename = wstr;
s.title = 0;
s.ext = NULL;
s.length = -1;
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILEW);
return 1;
}
INT_PTR PlayPlaylist(INT_PTR treeId)
{
if (LoadPlaylist(treeId))
{
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_STARTPLAY);
return 1;
}
else
return 0;
}

View File

@ -0,0 +1,63 @@
#include "main.h"
static HMENU last_playlistscmdmenu = NULL;
static WNDPROC PE_oldWndProc;
static WORD waCmdMenuID;
static BOOL CALLBACK PE_newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_COMMAND && wParam > 45000 && wParam < 55000)
{
if (LoadPlaylist(wParam - 45000))
return 0;
}
else if (uMsg == WM_INITMENUPOPUP)
{
HMENU hmenuPopup = (HMENU) wParam;
if (hmenuPopup == wa_playlists_cmdmenu)
{
if (!waCmdMenuID)
{
waCmdMenuID = (WORD)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_REGISTER_LOWORD_COMMAND);
}
if (last_playlistscmdmenu)
{
RemoveMenu(wa_playlists_cmdmenu, waCmdMenuID, MF_BYCOMMAND);
DestroyMenu(last_playlistscmdmenu);
last_playlistscmdmenu = NULL;
}
mlGetTreeStruct mgts = { 3001, 45000, -1};
last_playlistscmdmenu = (HMENU)SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM) &mgts, ML_IPC_GETTREE);
if (last_playlistscmdmenu)
{
MENUITEMINFOW menuItem = {sizeof(MENUITEMINFOW), MIIM_SUBMENU | MIIM_ID | MIIM_TYPE, MFT_STRING,
MFS_ENABLED, waCmdMenuID, last_playlistscmdmenu, NULL, NULL, NULL,
WASABI_API_LNGSTRINGW(IDS_OPEN_PLAYLIST_FROM_ML), 0};
// if there's no playlists then let the user know this
if(!AGAVE_API_PLAYLISTS->GetCount())
{
wchar_t buf[64] = {0};
DestroyMenu(last_playlistscmdmenu);
menuItem.hSubMenu = last_playlistscmdmenu = CreateMenu();
InsertMenuW(menuItem.hSubMenu, 0, MF_BYPOSITION | MF_STRING | MF_GRAYED, 0, WASABI_API_LNGSTRINGW_BUF(IDS_NO_PLAYLIST_IN_LIBRARY,buf,64));
}
InsertMenuItemW(wa_playlists_cmdmenu, 1, TRUE, &menuItem);
}
}
}
return CallWindowProc(PE_oldWndProc, hwndDlg, uMsg, wParam, lParam);
}
static HWND hwnd_pe = NULL;
void HookPlaylistEditor()
{
hwnd_pe =(HWND)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,IPC_GETWND_PE,IPC_GETWND);
if (hwnd_pe)
PE_oldWndProc=(WNDPROC) SetWindowLongPtr(hwnd_pe,GWLP_WNDPROC,(LONG_PTR)PE_newWndProc);
}
void UnhookPlaylistEditor()
{
SetWindowLongPtr(hwnd_pe,GWLP_WNDPROC,(LONG_PTR)PE_oldWndProc);
}

View File

@ -0,0 +1,185 @@
#include "main.h"
#include "replicant/nu/AutoWide.h"
#include "replicant/nu/AutoLock.h"
#include <algorithm>
#include <strsafe.h>
using namespace Nullsoft::Utility;
TREE_TO_GUID_MAP tree_to_guid_map;
bool FindTreeItem( INT_PTR treeId )
{
TREE_TO_GUID_MAP::iterator itr = tree_to_guid_map.find( treeId );
return itr != tree_to_guid_map.end();
}
void MakeTree( PlaylistInfo &playlist )
{
NAVINSERTSTRUCT nis = { 0 };
nis.item.cbSize = sizeof( NAVITEM );
nis.item.pszText = const_cast<wchar_t *>( AGAVE_API_PLAYLISTS->GetName( playlist.GetIndex() ) );
nis.item.mask = NIMF_TEXT | NIMF_IMAGE | NIMF_IMAGESEL | NIMF_ITEMID;
nis.item.id = playlist.treeId = 3002 + playlist.GetIndex();
nis.hParent = playlistItem;
if ( playlists_CloudInstalled() )
nis.item.iImage = nis.item.iSelectedImage = ( !playlist.GetCloud() ? normalimage : cloudImage );
else
nis.item.iImage = nis.item.iSelectedImage = normalimage;
if ( MLNavCtrl_InsertItem( plugin.hwndLibraryParent, &nis ) )
tree_to_guid_map[ playlist.treeId ] = playlist.playlist_guid;
}
void UpdateTree( PlaylistInfo &playlist, int tree_id )
{
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
size_t index = playlist.GetIndex();
MLTREEITEMW updatedItem = { NULL,MLTI_TEXT,NULL };
updatedItem.id = tree_id;
updatedItem.title = const_cast<wchar_t *>( AGAVE_API_PLAYLISTS->GetName( index ) );
updatedItem.imageIndex = ( !playlist.GetCloud() ? imgPL : imgCloudPL );
mediaLibrary.SetTreeItem( updatedItem );
tree_to_guid_map[ tree_id ] = playlist.playlist_guid;
}
void AddPlaylist( int callback, const wchar_t *title, const wchar_t *filename, bool makeTree, int cloud, size_t numItems, uint64_t length )
{
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
wchar_t fullFilename[ MAX_PATH ] = { 0 };
if ( PathIsFileSpecW( filename ) )
PathCombineW( fullFilename, g_path, filename );
else
lstrcpynW( fullFilename, filename, MAX_PATH );
size_t newIndex = AGAVE_API_PLAYLISTS->AddPlaylist_NoCallback( fullFilename, title );
// try to get a valid length of the playlist time
// (important for the playlists view otherwise looks silly with just the number shown)
if ( !length )
{
length = AGAVE_API_PLAYLISTMANAGER->GetLongLengthMilliseconds( fullFilename );
if ( length > 0 ) length /= 1000;
else length = 0;
}
if ( cloud )
AGAVE_API_PLAYLISTS->SetInfo( newIndex, api_playlists_cloud, &cloud, sizeof( cloud ) );
if ( numItems > 0 )
AGAVE_API_PLAYLISTS->SetInfo( newIndex, api_playlists_itemCount, &numItems, sizeof( numItems ) );
if ( length > 0 )
AGAVE_API_PLAYLISTS->SetInfo( newIndex, api_playlists_totalTime, &length, sizeof( length ) );
if ( callback )
WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_ADDED, newIndex, ( callback - 1 ) );
}
bool LoadOldPlaylists()
{
bool erased = false;
int nb = g_config->ReadInt( L"query_num", 0 );
for ( int i = 0; i < nb; i++ )
{
wchar_t qn[ 128 ] = { 0 }, qv[ 128 ] = { 0 }, qm[ 128 ] = { 0 }, qmet[ 128 ] = { 0 };
StringCchPrintfW( qn, 128, L"query%i_name", i + 1 );
StringCchPrintfW( qv, 128, L"query%i_val", i + 1 );
StringCchPrintfW( qm, 128, L"query%i_mode", i + 1 );
StringCchPrintfW( qmet, 128, L"query%i_meta", i + 1 );
int queryMode = g_config->ReadInt( qm, 0 );
if ( queryMode == 32 )
{
wchar_t *name = g_config->ReadString( qn, NULL );
if ( !name )
continue;
name = _wcsdup( name );
wchar_t *val = g_config->ReadString( qv, NULL );
if ( val )
val = _wcsdup( val );
wchar_t filename[ MAX_PATH ] = { 0 };
PathCombineW( filename, g_path, val );
size_t numItems = AGAVE_API_PLAYLISTMANAGER->CountItems( filename );
AddPlaylist( true, name, filename, ADD_TO_TREE, AddToCloud(), numItems );
g_config->WriteString( qn, NULL );
g_config->WriteString( qv, NULL );
g_config->WriteString( qm, NULL );
g_config->WriteString( qmet, NULL );
erased = true;
free( name );
free( val );
}
}
return erased;
}
void LoadPlaylists()
{
bool loadedOld = LoadOldPlaylists();
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
size_t count = AGAVE_API_PLAYLISTS->GetCount();
normalimage = mediaLibrary.AddTreeImageBmp( IDB_TREEITEM_PLAYLIST );
cloudImage = mediaLibrary.AddTreeImageBmp( IDB_TREEITEM_CLOUD_PLAYLIST );
for ( size_t i = 0; i != count; i++ )
{
PlaylistInfo info( i );
if ( info.Valid() )
MakeTree( info );
}
if ( loadedOld )
AGAVE_API_PLAYLISTS->Flush();
}
void UpdatePlaylists()
{
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
size_t count = AGAVE_API_PLAYLISTS->GetCount();
normalimage = mediaLibrary.AddTreeImageBmp( IDB_TREEITEM_PLAYLIST );
cloudImage = mediaLibrary.AddTreeImageBmp( IDB_TREEITEM_CLOUD_PLAYLIST );
for ( size_t i = 0; i != count; i++ )
{
PlaylistInfo info( i );
if ( info.Valid() )
UpdateTree( info, info.treeId );
}
if ( IsWindow( currentView ) )
PostMessage( currentView, WM_APP + 101, 0, 0 );
}
void Playlist_importFromWinamp()
{
SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_WRITEPLAYLIST );
const wchar_t *m3udir = (const wchar_t *)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETM3UDIRECTORYW );
wchar_t s[ MAX_PATH ] = { 0 };
PathCombineW( s, m3udir, L"winamp.m3u8" );
wchar_t filename[ 1024 + 256 ] = { 0 };
wchar_t *filenameptr = createPlayListDBFileName( filename );
wchar_t gs[ MAX_PATH ] = { 0 };
PathCombineW( gs, g_path, filenameptr );
size_t numItems = AGAVE_API_PLAYLISTMANAGER->Copy( gs, s );
AddPlaylist( true, WASABI_API_LNGSTRINGW( IDS_IMPORTED_PLAYLIST ), gs, ADD_TO_TREE, ( !( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) ? AddToCloud() : 0 ), numItems );
AGAVE_API_PLAYLISTS->Flush();
}

View File

@ -0,0 +1,6 @@
#ifndef NULLSOFT_ML_PLAYLISTS_PLAYLISTS_H
#define NULLSOFT_ML_PLAYLISTS_PLAYLISTS_H
#include "PlaylistInfo.h"
#endif

View File

@ -0,0 +1,88 @@
#include "Main.h"
#include "playlistsXML.h"
#include "../nu/AutoChar.h"
void PlaylistsXML::StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, api_xmlreaderparams *params)
{
const wchar_t *filename = params->getItemValue(L"filename");
const wchar_t *title = params->getItemValue(L"title");
const wchar_t *countString = params->getItemValue(L"songs");
int numItems = 0;
if (countString && *countString)
numItems = _wtoi(countString);
const wchar_t *lengthString = params->getItemValue(L"seconds");
int length = -1000;
if (lengthString && *lengthString)
length = _wtoi(lengthString);
AddPlaylist(title, filename, true, numItems, length);
}
void PlaylistsXML::LoadFile(const wchar_t *filename)
{
if (!parser)
return ; // no sense in continuing if there's no parser available
HANDLE file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
if (file == INVALID_HANDLE_VALUE)
return ;
char data[1024] = {0};
DWORD bytesRead;
while (true)
{
if (ReadFile(file, data, 1024, &bytesRead, NULL) && bytesRead)
{
if (parser->xmlreader_feed(data, bytesRead) != API_XML_SUCCESS)
{
CloseHandle(file);
return;
}
}
else
break;
}
CloseHandle(file);
parser->xmlreader_feed(0, 0);
}
PlaylistsXML::PlaylistsXML(): parser(0), parserFactory(0)
{
parserFactory = WASABI_API_SVC->service_getServiceByGuid(api_xmlGUID);
if (parserFactory)
parser = (api_xml *)parserFactory->getInterface();
if (parser)
{
parser->xmlreader_registerCallback(L"Winamp:Playlists\fplaylist", this);
parser->xmlreader_registerCallback(L"playlists\fplaylist", this);
parser->xmlreader_open();
}
}
PlaylistsXML::~PlaylistsXML()
{
if (parser)
{
parser->xmlreader_unregisterCallback(this);
parser->xmlreader_close();
}
if (parserFactory && parser)
parserFactory->releaseInterface(parser);
parserFactory = 0;
parser = 0;
}
#ifdef CBCLASS
#undef CBCLASS
#endif
#define CBCLASS PlaylistsXML
START_DISPATCH;
VCB(ONSTARTELEMENT, StartTag)
END_DISPATCH;

View File

@ -0,0 +1,27 @@
#ifndef NULLSOFT_ML_PLAYLISTS_PLAYLISTSXML_H
#define NULLSOFT_ML_PLAYLISTS_PLAYLISTSXML_H
#include "../xml/api_xml.h"
#include "../xml/api_xmlreadercallback.h"
#include "api.h"
#include <api/service/waServiceFactory.h>
class PlaylistsXML : public api_xmlreadercallback
{
public:
PlaylistsXML();
~PlaylistsXML();
void LoadFile(const wchar_t *filename);
private:
RECVS_DISPATCH;
/* XML callbacks */
void StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, api_xmlreaderparams *params);
api_xml *parser;
waServiceFactory *parserFactory;
};
#endif

View File

@ -0,0 +1,527 @@
#include "main.h"
#include "resource.h"
#include <windows.h>
#include <commctrl.h>
#include "SendTo.h"
#include "ml_local/api_mldb.h"
#include "ml_pmp/pmp.h"
#include "replicant/nswasabi/ReferenceCounted.h"
#include "replicant/nx/win/nxstring.h"
#include "replicant/nu/AutoChar.h"
#include "nu/AutoCharFn.h"
#include "nu/menushortcuts.h"
#include <api/syscb/callbacks/syscb.h>
#include <api/syscb/callbacks/browsercb.h>
using namespace Nullsoft::Utility;
INT_PTR lastActiveID = 0;
SendToMenu treeViewSendTo;
HWND currentView = 0;
int playlists_ContextMenu(INT_PTR param1, HWND hHost, POINTS pts);
LRESULT pluginHandleIpcMessage(int msg, WPARAM param)
{
return SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, param, msg);
}
INT_PTR pluginMessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3)
{
switch (message_type)
{
case ML_MSG_NO_CONFIG:
return TRUE;
case ML_MSG_TREE_ONCREATEVIEW:
if (param1 == playlistsTreeId)
{
return (INT_PTR)(currentView = WASABI_API_CREATEDIALOGW(IDD_VIEW_PLAYLISTS, (HWND)param2, view_playlistsDialogProc));
}
else // if param1 is a valid playlist
{
if (FindTreeItem(param1))
{
lastActiveID = param1;
// TODO: what if it's an ifc_playlist provided from elsewhere instead of just a filename?
return (INT_PTR)(currentView = WASABI_API_CREATEDIALOGPARAMW(IDD_VIEW_PLAYLIST, (HWND)param2, view_playlistDialogProc, lastActiveID));
}
}
break;
case ML_MSG_ONSENDTOBUILD:
return playlists_BuildSendTo(param1, param2);
case ML_MSG_ONSENDTOSELECT:
return playlists_OnSendTo(param1, param2, param3);
case ML_MSG_TREE_ONCLICK:
return playlists_OnClick(param1, param2, (HWND)param3);
case ML_MSG_NAVIGATION_CONTEXTMENU:
{
HNAVITEM hItem = (HNAVITEM)param1;
HNAVITEM myItem = MLNavCtrl_FindItemById(plugin.hwndLibraryParent, playlistsTreeId);
if (hItem == myItem)
{
return playlists_ContextMenu(param1, (HWND)param2, MAKEPOINTS(param3));
}
else
{
NAVITEM nvItem = {sizeof(NAVITEM),hItem,NIMF_ITEMID,};
MLNavItem_GetInfo(plugin.hwndLibraryParent, &nvItem);
if (FindTreeItem(nvItem.id))
{
HWND wnd = (HWND)param2;
sendToIgnoreID = nvItem.id;
HMENU menu = GetSubMenu(g_context_menus, 0),
sendToMenu = GetSubMenu(menu, 2);
treeViewSendTo.AddHere(wnd, sendToMenu, ML_TYPE_FILENAMES, 1, (ML_TYPE_PLAYLIST+1)); // we're going to lie about the type for now
// make sure that we call init on this otherwise the sendto menu will most likely fail
treeViewSendTo.InitPopupMenu((WPARAM)sendToMenu);
// tweaked to not fudge on the ml tree
EnableMenuItem(menu, IDC_PLAY, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(menu, IDC_ENQUEUE, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(menu, IDC_DELETE, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(menu, ID_QUERYMENU_ADDNEWQUERY, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(menu, IDC_RENAME, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(menu, IDC_ENQUEUE, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(menu, 2, MF_BYPOSITION | MF_ENABLED);
EnableMenuItem(menu, IDC_VIEWLIST, MF_BYCOMMAND | (nvItem.id != lastActiveID ? MF_ENABLED : MF_DISABLED));
HMENU cloud_hmenu = (HMENU)0x666;
size_t index = 0;
if (playlists_CloudAvailable())
{
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
PlaylistInfo info;
if (info.Associate(nvItem.id))
{
ReferenceCountedNXString uid;
NXStringCreateWithFormatting(&uid, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
(int)info.playlist_guid.Data1, (int)info.playlist_guid.Data2,
(int)info.playlist_guid.Data3, (int)info.playlist_guid.Data4[0],
(int)info.playlist_guid.Data4[1], (int)info.playlist_guid.Data4[2],
(int)info.playlist_guid.Data4[3], (int)info.playlist_guid.Data4[4],
(int)info.playlist_guid.Data4[5], (int)info.playlist_guid.Data4[6],
(int)info.playlist_guid.Data4[7]);
index = info.GetIndex();
WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_GET_CLOUD_STATUS, (intptr_t)uid->string, (intptr_t)&cloud_hmenu);
if (cloud_hmenu && cloud_hmenu != (HMENU)0x666)
{
MENUITEMINFOW m = {sizeof(m), MIIM_TYPE | MIIM_ID | MIIM_SUBMENU, MFT_SEPARATOR, 0};
m.wID = CLOUD_SOURCE_MENUS - 1;
InsertMenuItemW(menu, 3, TRUE, &m);
wchar_t a[100] = {0};
m.fType = MFT_STRING;
m.dwTypeData = WASABI_API_LNGSTRINGW_BUF(IDS_CLOUD_SOURCES, a, 100);
m.wID = CLOUD_SOURCE_MENUS;
m.hSubMenu = cloud_hmenu;
InsertMenuItemW(menu, 4, TRUE, &m);
}
}
}
bool swapPlayEnqueue=false;
if (g_config->ReadInt(L"enqueuedef", 0) == 1)
{
SwapPlayEnqueueInMenu(menu);
swapPlayEnqueue=true;
}
{
HACCEL accel = WASABI_API_LOADACCELERATORSW(IDR_VIEW_PL_ACCELERATORS);
int size = CopyAcceleratorTable(accel,0,0);
AppendMenuShortcuts(menu, &accel, size, MSF_REPLACE);
}
if (swapPlayEnqueue)
SwapPlayEnqueueInMenu(menu);
POINT pt;
POINTSTOPOINT(pt, MAKEPOINTS(param3));
if (-1 == pt.x || -1 == pt.y)
{
NAVITEMGETRECT itemRect;
itemRect.fItem = FALSE;
itemRect.hItem = hItem;
if (MLNavItem_GetRect(plugin.hwndLibraryParent, &itemRect))
{
MapWindowPoints(wnd, HWND_DESKTOP, (POINT*)&itemRect.rc, 2);
pt.x = itemRect.rc.left + 2;
pt.y = itemRect.rc.top + 2;
}
}
int r = Menu_TrackPopup(plugin.hwndLibraryParent, menu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, pt.x, pt.y, wnd, NULL);
switch (r)
{
case IDC_PLAY:
{
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
PlaylistInfo info;
if (info.Associate(nvItem.id))
{
playlist_SaveGUID(info.playlist_guid);
mediaLibrary.PlayFile(info.GetFilename());
}
}
break;
case IDC_ENQUEUE:
{
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
PlaylistInfo info;
if (info.Associate(nvItem.id))
{
playlist_SaveGUID(info.playlist_guid);
mediaLibrary.EnqueueFile(info.GetFilename());
}
}
break;
case IDC_NEWPLAYLIST:
playlists_Add(wnd);
break;
case IDC_DELETE:
DeletePlaylist(tree_to_guid_map[nvItem.id], wnd, true);
break;
case IDC_RENAME:
RenamePlaylist(tree_to_guid_map[nvItem.id], wnd);
break;
case IDC_VIEWLIST:
mediaLibrary.SelectTreeItem(nvItem.id);
break;
default:
{
if (treeViewSendTo.WasClicked(r))
{
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
PlaylistInfo info;
if (info.Associate(nvItem.id))
{
playlist_SaveGUID(info.playlist_guid);
info.Refresh();
mlPlaylist sendToPlaylist;
sendToPlaylist.filename = info.GetFilename();
sendToPlaylist.title = info.GetName();
sendToPlaylist.numItems = info.GetSize();
sendToPlaylist.length = info.GetLength();
if (treeViewSendTo.SendPlaylist(&sendToPlaylist) != 1)
{
// didn't like that
// let's try this way
wchar_t filenames[MAX_PATH + 1] = {0};
lstrcpyn(filenames, info.GetFilename(), MAX_PATH);
filenames[lstrlen(filenames) + 1] = 0;
treeViewSendTo.SendFilenames(filenames);
}
}
}
else
{
if (r >= CLOUD_SOURCE_MENUS && r < CLOUD_SOURCE_MENUS_PL_UPPER) // deals with cloud specific menus
{
// 0 = no change
// 1 = adding to cloud
// 2 = added locally
// 4 = removed
int mode = -(int)index;
WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_PROCESS_CLOUD_STATUS, (intptr_t)r, (intptr_t)&mode);
if (mode > 0)
{
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
PlaylistInfo info;
if (info.Associate(nvItem.id))
{
info.SetCloud((mode == 1 ? 1 : 0));
AGAVE_API_PLAYLISTS->Flush();
UpdatePlaylists();
}
}
}
}
}
}
treeViewSendTo.Cleanup();
sendToIgnoreID = 0;
if (cloud_hmenu && cloud_hmenu != (HMENU)0x666)
{
DeleteMenu(menu, CLOUD_SOURCE_MENUS - 1, MF_BYCOMMAND);
DeleteMenu(menu, CLOUD_SOURCE_MENUS, MF_BYCOMMAND);
DestroyMenu(cloud_hmenu);
}
return TRUE;
}
}
}
return FALSE;
case ML_MSG_TREE_ONKEYDOWN:
return playlists_OnKeyDown(param1, (NMTVKEYDOWN *)param2, (HWND)param3);
case ML_MSG_TREE_ONDRAG:
return playlists_OnDrag(param1, (POINT *)param2, (int *)param3);
case ML_MSG_TREE_ONDROP:
return playlists_OnDrop(param1, (POINT *)param2, param3);
case ML_MSG_TREE_ONDROPTARGET:
return playlists_OnDropTarget(param1, param2, param3);
case ML_MSG_PLAYING_FILE:
lstrcpynW(current_playing, (wchar_t*)param1, FILENAME_SIZE);
if (IsWindow(currentView)) PostMessage(currentView, WM_APP + 103, (WPARAM)current_playing, 0);
return FALSE;
case ML_MSG_WRITE_CONFIG:
if (param1)
{
// only save the ml playlists if saving winamp.m3u/m3u8
// else this will happen everytime the prefs are closed
AGAVE_API_PLAYLISTS->Flush();
}
return FALSE;
case ML_MSG_VIEW_PLAY_ENQUEUE_CHANGE:
{
enqueuedef = param1;
groupBtn = param2;
PostMessage(currentView, WM_APP + 104, param1, param2);
return 0;
}
}
return 0;
}
void myOpenURL(HWND hwnd, wchar_t *loc)
{
if (loc)
{
bool override=false;
WASABI_API_SYSCB->syscb_issueCallback(SysCallback::BROWSER, BrowserCallback::ONOPENURL, reinterpret_cast<intptr_t>(loc), reinterpret_cast<intptr_t>(&override));
if (!override)
ShellExecuteW(hwnd, L"open", loc, NULL, NULL, SW_SHOWNORMAL);
}
}
int playlists_ContextMenu( INT_PTR param1, HWND hHost, POINTS pts )
{
POINT pt;
POINTSTOPOINT( pt, pts );
if ( -1 == pt.x || -1 == pt.y )
{
HNAVITEM hItem = (HNAVITEM)param1;
NAVITEMGETRECT itemRect;
itemRect.fItem = FALSE;
itemRect.hItem = hItem;
if ( MLNavItem_GetRect( plugin.hwndLibraryParent, &itemRect ) )
{
MapWindowPoints( hHost, HWND_DESKTOP, (POINT *)&itemRect.rc, 2 );
pt.x = itemRect.rc.left + 2;
pt.y = itemRect.rc.top + 2;
}
}
HMENU menu = GetSubMenu( g_context_menus, 1 );
int r = Menu_TrackPopup( plugin.hwndLibraryParent, menu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_NONOTIFY, pt.x, pt.y, hHost, NULL );
switch ( r )
{
case IDC_NEWPLAYLIST:
playlists_Add( hHost );
break;
case IDC_IMPORT_PLAYLIST_FROM_FILE:
Playlist_importFromFile( hHost );
break;
case IDC_IMPORT_WINAMP_PLAYLIST:
Playlist_importFromWinamp();
break;
case ID_PLAYLISTSMENU_IMPORTPLAYLISTFROMFOLDERS:
Playlist_importFromFolders( hHost );
break;
case ID_SORTPLAYLIST_TITLE_A_Z:
playlists_Sort( SORT_TITLE_ASCENDING );
break;
case ID_SORTPLAYLIST_TITLE_Z_A:
playlists_Sort( SORT_TITLE_DESCENDING );
break;
case ID_SORTPLAYLIST_NUMBEROFITEMSASCENDING:
playlists_Sort( SORT_NUMBER_ASCENDING );
break;
case ID_SORTPLAYLIST_NUMBEROFITEMSDESCENDING:
playlists_Sort( SORT_NUMBER_DESCENDING );
break;
case ID_PLAYLISTS_HELP:
myOpenURL( hHost, L"https://help.winamp.com/hc/articles/8109547717268-Winamp-Playlists" );
break;
}
Sleep( 100 );
MSG msg;
while ( PeekMessage( &msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE ) ); //eat return
return TRUE;
}
// param1 = param of tree item, param2 = action type (below), param3 = HWND of main window
INT_PTR playlists_OnClick(INT_PTR treeId, int clickType, HWND wnd)
{
switch (clickType)
{
case ML_ACTION_DBLCLICK:
case ML_ACTION_ENTER:
{
if (FindTreeItem(treeId))
{
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
PlaylistInfo info;
if (info.Associate(treeId))
{
playlist_SaveGUID(info.playlist_guid);
bool flip = (clickType==ML_ACTION_ENTER) && (GetAsyncKeyState(VK_SHIFT)&0x8000);
if ((!!(g_config->ReadInt(L"enqueuedef", 0) == 1)) ^ flip)
mediaLibrary.EnqueueFile(info.GetFilename());
else
mediaLibrary.PlayFile(info.GetFilename());
}
return 1;
}
}
break;
}
return 0;
}
int playlists_OnKeyDown(int treeId, NMTVKEYDOWN *p, HWND hwndDlg)
{
int ctrl = (GetAsyncKeyState(VK_CONTROL)&0x8000);
int shift = (GetAsyncKeyState(VK_SHIFT)&0x8000);
if (treeId == playlistsTreeId)
{
switch (p->wVKey)
{
case VK_INSERT:
{
if (shift && !ctrl) playlists_Add(plugin.hwndLibraryParent); return 1;
}
}
}
else if (FindTreeItem(treeId))
{
switch (p->wVKey)
{
case VK_F2: if (!shift && !ctrl) RenamePlaylist(tree_to_guid_map[treeId], plugin.hwndLibraryParent); return 1;
case VK_INSERT: if (shift && !ctrl) playlists_Add(plugin.hwndLibraryParent); return 1;
case VK_DELETE: if (!shift && !ctrl) DeletePlaylist(tree_to_guid_map[treeId], hwndDlg, true); return 1;
}
}
return 0;
}
int playlists_OnDrag(int treeId, POINT *pt, int *type)
{
if (FindTreeItem(treeId))
{
*type = ML_TYPE_FILENAMES;
return 1;
}
return 0;
}
int playlists_OnDrop(int treeId, POINT *pt, int destTreeId)
{
if (FindTreeItem(treeId))
{
AutoLockT<api_playlists> lock (AGAVE_API_PLAYLISTS);
PlaylistInfo info;
if (info.Associate(treeId))
{
if (destTreeId == playlistsTreeId)
{
mediaLibrary.RemoveTreeItem(info.treeId);
tree_to_guid_map.erase(info.treeId);
AGAVE_API_PLAYLISTS->MoveBefore(info.GetIndex(), 0);
// TODO: move most of this to PlaylistsCB
// TODO use the more native code like in MakeTree(..)
MLTREEITEMW src = {sizeof(MLTREEITEMW), };
src.title = const_cast<wchar_t *>(info.GetName());
src.hasChildren = 0;
src.parentId = playlistsTreeId;
src.id = destTreeId;
src.imageIndex = (!info.GetCloud() ? imgPL : imgCloudPL);
mediaLibrary.InsertTreeItem(src);
info.treeId = src.id;
tree_to_guid_map[info.treeId] = info.playlist_guid;
mediaLibrary.SelectTreeItem(info.treeId);
}
else if (FindTreeItem(destTreeId))
{
PlaylistInfo dest;
if (dest.Associate(destTreeId))
{
mediaLibrary.RemoveTreeItem(info.treeId);
tree_to_guid_map.erase(info.treeId);
AGAVE_API_PLAYLISTS->MoveBefore(info.GetIndex(), dest.GetIndex()+1);
// TODO: move most of this to PlaylistsCB
// TODO use the more native code like in MakeTree(..)
MLTREEITEMW src = {sizeof(MLTREEITEMW), };
src.title = const_cast<wchar_t *>(info.GetName());
src.hasChildren = 0;
src.parentId = playlistsTreeId;
src.id = destTreeId;
src.imageIndex = (!info.GetCloud() ? imgPL : imgCloudPL);
mediaLibrary.InsertTreeItem(src);
info.treeId = src.id;
tree_to_guid_map[info.treeId] = info.playlist_guid;
mediaLibrary.SelectTreeItem(info.treeId);
}
return 1;
}
else
{
playlist_SaveGUID(info.playlist_guid);
info.Refresh();
mlPlaylist pl;
pl.filename = info.GetFilename();
pl.length = info.GetLength();
pl.numItems = info.GetSize();
pl.title = info.GetName();
mlDropItemStruct m = {0};
m.p = *pt;
m.flags = 0;
m.type = ML_TYPE_PLAYLIST;
m.result = 0;
m.data = (void *) & pl;
m.name = 0;
pluginHandleIpcMessage(ML_IPC_HANDLEDROP, (WPARAM)&m);
/* TODO: fall back to this is result fails?
mlDropItemStruct m = {0};
m.p = *pt;
m.flags = ML_HANDLEDRAG_FLAG_NAME;
m.type=ML_TYPE_FILENAMES;
m.result = 0;
char filename[1025] = {0};
lstrcpynA(filename, AutoCharFn(playlists[index].filename), 1024);
filename[lstrlenA(filename)+1]=0;
m.data=(void *)filename;
AutoChar charTitle(playlists[index].title);
m.name = charTitle;
pluginHandleIpcMessage(ML_IPC_HANDLEDROP, (WPARAM)&m);
*/
}
}
}
return 0;
}

View File

@ -0,0 +1,177 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by ml_playlists.rc
//
#define IDS_IMPORT_PLAYLIST 0
#define IDS_APPEND_IMPORTED_PLAYLIST 1
#define IDS_LIBRARY_QUESTION 2
#define IDS_APPEND_ACTIVE_PLAYLIST 3
#define IDS_EXPORT_PLAYLIST 4
#define IDS_IMPORTED_PLAYLIST 6
#define IDS_ITEM 7
#define IDS_ADD_DIR_TO_PLAYLIST 8
#define IDS_ADD_FILES_TO_PLAYLIST 9
#define IDS_PLAYLISTS 11
#define IDS_MANAGE_PLAYLISTS 12
#define IDS_NEW_PLAYLIST 13
#define IDS_ENTER_A_NAME 14
#define IDS_ERROR 15
#define IDS_SENDTO_NEW_PLAYLIST 16
#define IDS_SENDTO_ML_PLAYLISTS 17
#define IDS_SENDTO_PLAYLIST 18
#define IDS_IMPORT_PLAYLIST_FROM_FOLDER 19
#define IDS_CONFIRM_DELETION 20
#define IDS_CONFIRMATION 21
#define IDS_PLAYLIST_TITLE 22
#define IDS_ITEMS 23
#define IDS_TIME 24
#define IDS_ERROR_DELETING_FILES 25
#define IDS_X_OF_X_SELECTED 26
#define IDS_X_SELECTED 27
#define IDS_ITEMS_LOWER 28
#define IDS_TITLE 29
#define IDS_NO_PLAYLIST_IN_LIBRARY 30
#define IDS_OPEN_PLAYLIST_FROM_ML 31
#define IDS_PLAYLIST_FROM_ML 32
#define IDS_PLAYLIST_ERROR 34
#define IDS_PLAYLIST_ERROR_TITLE 35
#define IDS_DAY 36
#define IDS_DAYS 37
#define IDB_TREEITEM_CLOUD_PLAYLIST 38
#define IDS_SENDTO_NEW_CLOUD_PLAYLIST 39
#define IDD_ADD_CLOUD_PLAYLIST 40
#define IDS_CLOUD_UNCHECKED 40
#define IDS_PL_FILE_MNGT 41
#define IDS_EXTERNAL_CHECKED 42
#define IDS_EXTERNAL_ALREADY_ADDED 43
#define IDS_CLOUD_SOURCES 44
#define IDS_UPLOAD_TO_CLOUD 45
#define IDS_AVAILABLE_IN_CLOUD 46
#define IDS_SOURCE_PL_MISSING 47
#define IDS_TRACK_AVAILABLE 48
#define IDS_UPLOAD_TO_SOURCE 49
#define IDS_STRING53 50
#define IDS_BROWSE_FOR_PLEDIT_ENTRY 50
#define IDD_VIEW_PLAYLISTS 101
#define IDR_MENU1 102
#define IDD_RENAME_PLAYLIST 103
#define IDD_ADD_PLAYLIST 104
#define IDD_VIEW_PLAYLIST 105
#define IDB_BITMAP1 106
#define IDC_CURSOR1 107
#define IDD_BROWSE_PLFLD 108
#define IDD_SELECT_PLAYLIST 110
#define IDD_EDIT_FN 111
#define IDR_VIEW_PL_ACCELERATORS 113
#define IDR_VIEW_PLS_ACCELERATORS 116
#define IDC_CUSTOM 1000
#define IDC_PLAYLIST_LIST 1001
#define IDC_VIEWLIST 1002
#define IDC_PLAY 1003
#define IDC_NAME 1003
#define IDC_ENQUEUE 1004
#define IDC_CREATENEWPL 1005
#define IDC_BURN 1005
#define IDC_IMPORT 1006
#define IDC_PLSTATUS 1006
#define IDC_SAVE 1007
#define IDC_ADD 1007
#define IDB_TREEITEM_PLAYLIST 1007
#define IDC_REM 1008
#define IDC_SEL 1009
#define IDC_MISC 1010
#define IDC_LIST 1011
#define IDC_PLAYLIST_EDITOR 1012
#define IDC_CHECK1 1013
#define IDC_SAVE_PL 1013
#define IDC_EXTERNAL 1014
#define IDC_OLD 1015
#define IDD_IMPORT_PLFLD 1015
#define IDC_NEW 1016
#define IDC_PLAYLISTS 1016
#define IDC_CLOUD 1017
#define IDC_OLD_TITLE 1017
#define IDC_NEW_TITLE 1018
#define IDC_PLAYLIST_EDIT_ENTRY_BROWSE 1020
#define ID_QUERYWND_PLAYQUERY 40001
#define ID_QUERYWND_ENQUEUEQUERY 40002
#define ID_MEDIAWND_ADDTOPLAYLIST 40003
#define ID_QUERYWND_EDIT 40004
#define ID_QUERYMENU_ADDNEWQUERY 40005
#define ID_QUERYWND_DELETE 40006
#define ID_QUERYMENU_ADDNEWPLAYLIST 40007
#define ID_PLAYLISTSMENU_IMPORTPLAYLISTFROMFILE 40008
#define ID_PLAYLISTSMENU_IMPORTACTIVECURRENTPLAYLIST 40009
#define ID_PLAYLISTSMENU_IMPORTPLAYLISTFROMFOLDERS 40010
#define ID_PLAYLISTSIMPORT_IMPORTPLAYLISTSFROMFOLDER 40011
#define ID_PLAYLISTSIMPORT_IMPORTACTIVECURRENTPLAYLIST 40012
#define ID_PLAYLISTSIMPORT_IMPORTPLAYLISTFROMFILE 40013
#define ID_PECONTEXT_PLAYSELECTION 40014
#define ID_PECONTEXT_ENQUEUESELECTION 40015
#define ID_PECONTEXT_DELETESELECTION 40016
#define ID_PEDITORMENUS_CROPSELECTEDITEMS 40017
#define ID_PE_ID3 40018
#define ID_PECONTEXT_SELECTNONE 40019
#define ID_PECONTEXT_INVERTSELECTION 40020
#define ID_PECONTEXT_SELECTALL 40021
#define ID_PE_NONEXIST 40022
#define ID_PE_REMOVEALL 40023
#define IDC_PLAYLIST_ADDLOC 40024
#define IDC_PLAYLIST_ADDDIR 40025
#define IDC_PLAYLIST_ADDMP3 40026
#define ID_PECONTEXT_RANDOMIZELIST 40027
#define ID_PECONTEXT_REVERSELIST 40028
#define ID_PE_S_PATH 40029
#define ID_PE_S_FILENAME 40030
#define ID_PE_S_TITLE 40031
#define ID_PEDITORMENUS_LIST_EXPORTPLAYLIST 40032
#define ID_PEDITORMENUS_LIST_IMPORTPLAYLISTFROMDISK 40033
#define ID_PEDITORMENUS_LIST_IMPORTACTIVEPLAYLIST 40034
#define IDC_EXPORT_PLAYLIST 40035
#define IDC_IMPORT_PLAYLIST_FROM_FILE 40036
#define IDC_IMPORT_WINAMP_PLAYLIST 40037
#define IDC_PLAYLIST_RANDOMIZE 40038
#define IDC_PLAYLIST_REVERSE 40039
#define IDC_PLAYLIST_SORT_FILENAME 40041
#define IDC_PLAYLIST_SORT_PATH 40042
#define IDC_PLAYLIST_SORT_TITLE 40043
#define IDC_ADD_LOCATION 40044
#define IDC_ADD_DIRECTORY 40045
#define IDC_ADD_FILES 40046
#define IDC_PLAYLIST_REMOVE_DEAD 40047
#define IDC_PLAYLIST_REMOVE_ALL 40048
#define IDC_PLAYLIST_SELECT_NONE 40051
#define IDC_PLAYLIST_INVERT_SELECTION 40052
#define IDC_PLAYLIST_SELECT_ALL 40053
#define IDC_PLAYLIST_VIEW_FILE_INFO 40054
#define IDC_PLAYLIST_DOWNLOAD_ENTRY 40055
#define IDC_RENAME 40056
#define IDC_DELETE 40057
#define IDC_CROP 40058
#define IDC_NEWPLAYLIST 40059
#define IDC_PLAYLIST_RESET_CACHE 40061
#define IDC_PLAYLIST_EDIT_ENTRY 40063
#define IDC_PLAYLIST_RECYCLE_SELECTED 40065
#define ID_SORTPLAYLIST_NUMBEROFITEMSASCENDING 40081
#define ID_SORTPLAYLIST_NUMBEROFITEMSDESCENDING 40082
#define ID_SORTPLAYLIST_TITLE_A_Z 40083
#define ID_SORTPLAYLIST_TITLE_Z_A 40084
#define ID_PLAYLISTSMENU_HELP 40085
#define ID_PLAYLISTS_HELP 40089
#define ID_RIGHTCLICK_EXPLOREITEM 40092
#define IDC_PLAYLIST_EXPLOREITEMFOLDER 40093
#define ID_PLAYLIST_EXPLOREITEMFOLD 40096
#define ID_PLAYLIST_EXPLOREITEMFOLDER 40097
#define ID_PLAYLIST_GENERATE_HTML 40098
#define IDS_NULLSOFT_PLAYLISTS 65534
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 54
#define _APS_NEXT_COMMAND_VALUE 40102
#define _APS_NEXT_CONTROL_VALUE 1021
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

View File

@ -0,0 +1,39 @@
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
#include "../../../Winamp/buildType.h"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,78,0,0
PRODUCTVERSION WINAMP_PRODUCTVER
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Winamp SA"
VALUE "FileDescription", "Winamp Media Library Plug-in"
VALUE "FileVersion", "1,78,0,0"
VALUE "InternalName", "Nullsoft Playlists"
VALUE "LegalCopyright", "Copyright <20> 2002-2023 Winamp SA"
VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
VALUE "OriginalFilename", "ml_playlists.dll"
VALUE "ProductName", "Winamp"
VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,90 @@
#include "main.h"
#include "../Winamp/wa_ipc.h"
#include "replicant/nu/AutoLock.h"
#include <algorithm>
using namespace Nullsoft::Utility;
static WNDPROC waProc = 0;
extern HMENU wa_play_menu;
static HMENU last_playlistsmenu = NULL;
WORD waMenuID = 0;
extern int IPC_LIBRARY_PLAYLISTS_REFRESH, IPC_CLOUD_ENABLED;
LRESULT WINAPI WinampProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_COMMAND || msg == WM_SYSCOMMAND)
{
if (LOWORD(wParam) == WINAMP_MANAGEPLAYLISTS)
{
mediaLibrary.ShowMediaLibrary();
mediaLibrary.SelectTreeItem(playlistsTreeId);
return 1;
}
else if (msg == WM_COMMAND && wParam > 45000 && wParam < 55000)
{
INT_PTR treeId = wParam - 45000;
if (FindTreeItem(treeId))
{
mediaLibrary.SwitchToPluginView(treeId);
}
}
else if (msg == WM_COMMAND && wParam > 55000 && wParam < 65000)
{
if (PlayPlaylist(wParam - 55000))
return 0;
}
}
else if (msg == WM_INITMENUPOPUP)
{
HMENU hmenuPopup = (HMENU) wParam;
if (hmenuPopup == wa_play_menu)
{
if (last_playlistsmenu)
{
RemoveMenu(wa_play_menu, waMenuID, MF_BYCOMMAND);
DestroyMenu(last_playlistsmenu);
last_playlistsmenu = NULL;
}
mlGetTreeStruct mgts = { 3001, 55000, -1};
last_playlistsmenu = (HMENU)SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM) &mgts, ML_IPC_GETTREE);
if (last_playlistsmenu)
{
MENUITEMINFOW menuItem = {sizeof(MENUITEMINFOW), MIIM_SUBMENU | MIIM_ID | MIIM_TYPE, MFT_STRING,
MFS_ENABLED, waMenuID, last_playlistsmenu, NULL, NULL, NULL,
WASABI_API_LNGSTRINGW(IDS_PLAYLIST_FROM_ML), 0};
// if there's no playlists then let the user know this
if(!AGAVE_API_PLAYLISTS->GetCount())
{
wchar_t buf[64] = {0};
DestroyMenu(last_playlistsmenu);
menuItem.hSubMenu = last_playlistsmenu = CreateMenu();
InsertMenuW(menuItem.hSubMenu, 0, MF_BYPOSITION | MF_STRING | MF_GRAYED, 0, WASABI_API_LNGSTRINGW_BUF(IDS_NO_PLAYLIST_IN_LIBRARY,buf,64));
}
InsertMenuItemW(wa_play_menu, GetMenuItemCount(wa_play_menu), TRUE, &menuItem);
}
}
}
else if (msg == WM_WA_IPC && lParam == IPC_LIBRARY_PLAYLISTS_REFRESH)
{
// refresh the status of the tree items e.g. when made
// being made into a cloud playlist or remove from it
UpdatePlaylists();
}
else if (msg == WM_WA_IPC && lParam == IPC_CLOUD_ENABLED)
{
cloud_avail = 1;
if (IsWindow(currentView)) PostMessage(currentView, WM_APP + 102, 0, 0);
}
if (waProc)
return CallWindowProcW(waProc, hwnd, msg, wParam, lParam);
else
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void Hook(HWND winamp)
{
if (IsWindow(winamp))
waProc = (WNDPROC)SetWindowLongPtrW(winamp, GWLP_WNDPROC, (LONG_PTR)WinampProcedure);
}