1953 lines
55 KiB
C++
1953 lines
55 KiB
C++
#include <windows.h>
|
|
#include <shlwapi.h>
|
|
#include <shlobj.h>
|
|
#include <shellapi.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "main.h"
|
|
#include "nu/listview.h"
|
|
|
|
#include "resource.h"
|
|
#include "Playlist.h"
|
|
#include "replicant/nu/AutoChar.h"
|
|
#include "../../General/gen_ml/ml_ipc.h"
|
|
#include "SendTo.h"
|
|
#include "api__ml_playlists.h"
|
|
#include "../../General/gen_ml/ml_ipc_0313.h"
|
|
#include "nu/menushortcuts.h"
|
|
#include "ml_local/api_mldb.h"
|
|
#include "ml_pmp/pmp.h"
|
|
#include "replicant/nswasabi/ReferenceCounted.h"
|
|
#include "replicant/nx/win/nxstring.h"
|
|
|
|
void playlist_UpdateButtonText( HWND hwndDlg, int enqueuedef );
|
|
BOOL playlist_ButtonPopupMenu( HWND hwndDlg, int buttonId, HMENU menu, int flags = 0 );
|
|
|
|
static std::vector<GUID> playlistGUIDs;
|
|
using namespace Nullsoft::Utility;
|
|
SendToMenu sendTo;
|
|
static W_ListView m_playlistslist;
|
|
|
|
int root_is_drag_and_dropping = 0;
|
|
HINSTANCE cloud_hinst = 0;
|
|
static int last_item1 = -1;
|
|
int IPC_GET_CLOUD_HINST = -1;
|
|
int IPC_GET_CLOUD_ACTIVE = -1;
|
|
int cloud_avail = 0;
|
|
int normalimage = 0;
|
|
int cloudImage = 0;
|
|
|
|
static void AutoSizePlaylistColumns()
|
|
{
|
|
m_playlistslist.AutoSizeColumn( 2 );
|
|
m_playlistslist.AutoSizeColumn( 3 );
|
|
|
|
RECT channelRect;
|
|
GetClientRect( m_playlistslist.getwnd(), &channelRect );
|
|
|
|
ListView_SetColumnWidth( m_playlistslist.getwnd(), 0, channelRect.right - m_playlistslist.GetColumnWidth( 1 ) - m_playlistslist.GetColumnWidth( 2 ) - m_playlistslist.GetColumnWidth( 3 ) );
|
|
}
|
|
|
|
static bool opened = false, loaded = false;
|
|
|
|
void RefreshPlaylistsList()
|
|
{
|
|
if ( opened )
|
|
{
|
|
playlistGUIDs.clear();
|
|
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
|
|
size_t count = AGAVE_API_PLAYLISTS->GetCount();
|
|
playlistGUIDs.reserve( count );
|
|
|
|
for ( size_t i = 0; i < count; i++ )
|
|
playlistGUIDs.push_back( AGAVE_API_PLAYLISTS->GetGUID( i ) );
|
|
|
|
ListView_SetItemCount( m_playlistslist.getwnd(), playlistGUIDs.size() );
|
|
ListView_RedrawItems( m_playlistslist.getwnd(), 0, playlistGUIDs.size() - 1 );
|
|
}
|
|
}
|
|
|
|
void ImportPlaylist( const wchar_t *srcFilename, bool callback = false )
|
|
{
|
|
wchar_t l_src_filename[ MAX_PATH ] = { 0 };
|
|
lstrcpynW( l_src_filename, srcFilename, MAX_PATH );
|
|
|
|
wchar_t filename[ MAX_PATH ] = { 0 };
|
|
wchar_t *filenameptr = ( !g_config->ReadInt( L"external", 0 ) ? createPlayListDBFileName( filename ) : 0 );
|
|
size_t numItems = AGAVE_API_PLAYLISTMANAGER->Copy( filename, l_src_filename );
|
|
|
|
// get the filename of the imported playlist
|
|
PathRemoveExtensionW( l_src_filename );
|
|
PathStripPathW( l_src_filename );
|
|
|
|
// just incase we've had external playlists added / imported
|
|
// we spin through and abort trying to re-add any that match
|
|
if ( g_config->ReadInt( L"external", 0 ) )
|
|
{
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
|
|
size_t count = AGAVE_API_PLAYLISTS->GetCount();
|
|
for ( size_t i = 0; i != count; i++ )
|
|
{
|
|
PlaylistInfo info( i );
|
|
if ( info.Valid() )
|
|
{
|
|
if ( !lstrcmpiW( srcFilename, info.GetFilename() ) )
|
|
{
|
|
wchar_t titleStr[ 96 ] = { 0 };
|
|
MessageBox( currentView, WASABI_API_LNGSTRINGW( IDS_EXTERNAL_ALREADY_ADDED ), WASABI_API_LNGSTRINGW_BUF( IDS_PL_FILE_MNGT, titleStr, 96 ), MB_OK | MB_ICONWARNING );
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( l_src_filename[ 0 ] )
|
|
AddPlaylist( ( !callback ? 1 : 2 ), l_src_filename, ( !g_config->ReadInt( L"external", 0 ) ? filenameptr : srcFilename ), 1, g_config->ReadInt( L"cloud", 1 ), numItems );
|
|
else
|
|
AddPlaylist( ( !callback ? 1 : 2 ), WASABI_API_LNGSTRINGW( IDS_IMPORTED_PLAYLIST ), ( !g_config->ReadInt( L"external", 0 ) ? filenameptr : srcFilename ), 1, g_config->ReadInt( L"cloud", 1 ), numItems );
|
|
}
|
|
|
|
void playlists_ImportExternalPrompt( HWND hwndDlg )
|
|
{
|
|
// TODO decide if better to show the message on all changes or only once and
|
|
// then just leave the user to it in the future otherwise leave as it is
|
|
//if (!g_config->ReadInt("external_prompt", 0) && g_config->ReadInt("external", 0))
|
|
if ( !g_config->ReadInt( L"external", 0 ) )
|
|
{
|
|
wchar_t titleStr[ 96 ] = { 0 };
|
|
if ( MessageBox( hwndDlg, WASABI_API_LNGSTRINGW( IDS_EXTERNAL_CHECKED ), WASABI_API_LNGSTRINGW_BUF( IDS_PL_FILE_MNGT, titleStr, 96 ), MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2 ) == IDYES )
|
|
{
|
|
g_config->WriteInt( L"external", ( IsDlgButtonChecked( hwndDlg, IDC_EXTERNAL ) == BST_CHECKED ) );
|
|
}
|
|
else
|
|
{
|
|
CheckDlgButton( hwndDlg, IDC_EXTERNAL, g_config->ReadInt( L"external", 0 ) );
|
|
}
|
|
|
|
g_config->WriteInt( L"external_prompt", 1 );
|
|
}
|
|
else
|
|
{
|
|
g_config->WriteInt( L"external", ( IsDlgButtonChecked( hwndDlg, IDC_EXTERNAL ) == BST_CHECKED ) );
|
|
}
|
|
}
|
|
|
|
UINT_PTR CALLBACK Playlist_OFNHookProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
if ( uMsg == WM_INITDIALOG )
|
|
{
|
|
int cloud = playlists_CloudAvailable();
|
|
HWND cloudWnd = GetDlgItem( hwndDlg, IDC_CLOUD );
|
|
if ( IsWindow( cloudWnd ) )
|
|
{
|
|
ShowWindow( cloudWnd, cloud );
|
|
CheckDlgButton( hwndDlg, IDC_CLOUD, AddToCloud() );
|
|
}
|
|
|
|
CheckDlgButton( hwndDlg, IDC_EXTERNAL, g_config->ReadInt( L"external", 0 ) );
|
|
|
|
if ( !cloud )
|
|
{
|
|
HWND external = GetDlgItem( hwndDlg, IDC_EXTERNAL );
|
|
if ( IsWindow( external ) && IsWindow( cloudWnd ) )
|
|
{
|
|
RECT r = { 0 }, cl = { 0 };
|
|
GetWindowRect( external, &r );
|
|
GetWindowRect( cloudWnd, &cl );
|
|
ScreenToClient( hwndDlg, (LPPOINT)&r );
|
|
ScreenToClient( hwndDlg, (LPPOINT)&cl );
|
|
SetWindowPos( external, NULL, cl.left, r.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_SHOWWINDOW );
|
|
}
|
|
}
|
|
}
|
|
else if ( uMsg == WM_COMMAND )
|
|
{
|
|
switch ( LOWORD( wParam ) )
|
|
{
|
|
case IDC_CLOUD:
|
|
playlists_AddToCloudPrompt( hwndDlg );
|
|
return 1;
|
|
|
|
case IDC_EXTERNAL:
|
|
playlists_ImportExternalPrompt( hwndDlg );
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Playlist_importFromFile( HWND dlgparent )
|
|
{
|
|
wchar_t oldCurPath[ MAX_PATH ] = { 0 };
|
|
wchar_t newCurPath[ MAX_PATH ] = { 0 };
|
|
bool skipRes = false;
|
|
|
|
GetCurrentDirectoryW( MAX_PATH, oldCurPath );
|
|
|
|
retry:
|
|
wchar_t temp[ 1024 ] = { 0 };
|
|
wchar_t filter[ 1024 ] = { 0 };
|
|
AGAVE_API_PLAYLISTMANAGER->GetFilterList( filter, 1024 );
|
|
|
|
OPENFILENAMEW l = { sizeof( l ), 0 };
|
|
l.hwndOwner = dlgparent;
|
|
l.lpstrFilter = filter;
|
|
l.lpstrFile = temp;
|
|
l.nMaxFile = 1024;
|
|
l.lpstrTitle = WASABI_API_LNGSTRINGW( IDS_IMPORT_PLAYLIST );
|
|
l.lpstrDefExt = L"m3u";
|
|
l.lpstrInitialDir = WASABI_API_APP->path_getWorkingPath();
|
|
l.lpfnHook = Playlist_OFNHookProc;
|
|
l.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLEHOOK | OFN_ENABLESIZING | OFN_ENABLETEMPLATE;
|
|
l.lpTemplateName = MAKEINTRESOURCE( IDD_IMPORT_PLFLD );
|
|
l.hInstance = ( !skipRes ? WASABI_API_LNG_HINST : WASABI_API_ORIG_HINST );
|
|
|
|
if ( GetOpenFileNameW( &l ) )
|
|
{
|
|
GetCurrentDirectoryW( MAX_PATH, newCurPath );
|
|
WASABI_API_APP->path_setWorkingPath( newCurPath );
|
|
|
|
ImportPlaylist( temp );
|
|
}
|
|
else
|
|
{
|
|
// deals with the extra child dialog not being present in language packs
|
|
// so we re-spin and try to load the native version before just failing
|
|
DWORD res = CommDlgExtendedError();
|
|
if ( res == CDERR_NOTEMPLATE || res == CDERR_FINDRESFAILURE )
|
|
{
|
|
if ( !skipRes )
|
|
{
|
|
skipRes = true;
|
|
goto retry;
|
|
}
|
|
}
|
|
}
|
|
|
|
SetCurrentDirectoryW( oldCurPath );
|
|
}
|
|
|
|
void Playlists_ReplaceBadPathChars( LPWSTR pszPath )
|
|
{
|
|
if ( NULL == pszPath )
|
|
return;
|
|
|
|
while ( L'\0' != *pszPath )
|
|
{
|
|
switch ( *pszPath )
|
|
{
|
|
case L'?':
|
|
case L'/':
|
|
case L'\\':
|
|
case L':':
|
|
case L'*':
|
|
case L'\"':
|
|
case L'<':
|
|
case L'>':
|
|
case L'|':
|
|
*pszPath = L'_';
|
|
break;
|
|
default:
|
|
if ( *pszPath < 32 )
|
|
*pszPath = L'_';
|
|
break;
|
|
}
|
|
|
|
pszPath = CharNextW( pszPath );
|
|
}
|
|
}
|
|
|
|
void Playlist_export( HWND dlgparent, const wchar_t *name, const wchar_t *srcm3u )
|
|
{
|
|
wchar_t oldCurPath[ MAX_PATH ] = { 0 };
|
|
GetCurrentDirectoryW( MAX_PATH, oldCurPath );
|
|
|
|
wchar_t temp[ MAX_PATH ] = { 0 };
|
|
OPENFILENAMEW l = { sizeof( OPENFILENAMEW ), 0 };
|
|
l.hwndOwner = dlgparent;
|
|
l.hInstance = plugin.hDllInstance;
|
|
|
|
lstrcpynW( temp, name, MAX_PATH );
|
|
Playlists_ReplaceBadPathChars( temp );
|
|
|
|
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.lpstrInitialDir = WASABI_API_APP->path_getWorkingPath();
|
|
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->Copy( temp, srcm3u );
|
|
}
|
|
|
|
g_config->WriteInt( L"filter", l.nFilterIndex );
|
|
SetCurrentDirectoryW( oldCurPath );
|
|
}
|
|
|
|
void importPlaylistFolder( const wchar_t *path, int dorecurs )
|
|
{
|
|
wchar_t tmppath[ MAX_PATH ] = { 0 };
|
|
PathCombineW( tmppath, path, L"*" );
|
|
|
|
WIN32_FIND_DATAW d;
|
|
HANDLE h = FindFirstFileW( tmppath, &d );
|
|
if ( h == INVALID_HANDLE_VALUE )
|
|
return;
|
|
|
|
do
|
|
{
|
|
wchar_t l_playlist_folder[ MAX_PATH ] = { 0 };
|
|
PathCombineW( l_playlist_folder, path, d.cFileName );
|
|
|
|
if ( d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && lstrcmpW( d.cFileName, L"." ) && lstrcmpW( d.cFileName, L".." ) && dorecurs )
|
|
{
|
|
importPlaylistFolder( l_playlist_folder, dorecurs );
|
|
continue;
|
|
}
|
|
|
|
if ( AGAVE_API_PLAYLISTMANAGER->CanLoad( l_playlist_folder ) )
|
|
{
|
|
ImportPlaylist( l_playlist_folder, true );
|
|
}
|
|
} while ( FindNextFileW( h, &d ) != 0 );
|
|
|
|
if ( h != INVALID_HANDLE_VALUE )
|
|
FindClose( h );
|
|
}
|
|
|
|
void Shell_Free( void *p )
|
|
{
|
|
IMalloc *m;
|
|
SHGetMalloc( &m );
|
|
|
|
m->Free( p );
|
|
}
|
|
|
|
static INT_PTR CALLBACK browseCheckBoxProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
if ( uMsg == WM_INITDIALOG )
|
|
{
|
|
int cloud = playlists_CloudAvailable();
|
|
HWND cloudWnd = GetDlgItem( hwndDlg, IDC_CLOUD );
|
|
if ( IsWindow( cloudWnd ) )
|
|
{
|
|
ShowWindow( cloudWnd, cloud );
|
|
CheckDlgButton( hwndDlg, IDC_CLOUD, AddToCloud() );
|
|
}
|
|
|
|
if ( g_config->ReadInt( L"importplfoldrecurs", 1 ) )
|
|
CheckDlgButton( hwndDlg, IDC_CHECK1, BST_CHECKED );
|
|
|
|
CheckDlgButton( hwndDlg, IDC_CLOUD, AddToCloud() );
|
|
CheckDlgButton( hwndDlg, IDC_EXTERNAL, g_config->ReadInt( L"external", 0 ) );
|
|
}
|
|
|
|
if ( uMsg == WM_COMMAND )
|
|
{
|
|
if ( LOWORD( wParam ) == IDC_CHECK1 )
|
|
{
|
|
g_config->WriteInt( L"importplfoldrecurs", !!IsDlgButtonChecked( hwndDlg, IDC_CHECK1 ) );
|
|
}
|
|
else if ( LOWORD( wParam ) == IDC_CLOUD )
|
|
{
|
|
playlists_AddToCloudPrompt( hwndDlg );
|
|
}
|
|
else if ( LOWORD( wParam ) == IDC_EXTERNAL )
|
|
{
|
|
playlists_ImportExternalPrompt( hwndDlg );
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
BOOL CALLBACK browseEnumProc( HWND hwnd, LPARAM lParam )
|
|
{
|
|
wchar_t cl[ 32 ] = { 0 };
|
|
GetClassNameW( hwnd, cl, ARRAYSIZE( cl ) );
|
|
if ( !lstrcmpiW( cl, WC_TREEVIEW ) )
|
|
{
|
|
PostMessage( hwnd, TVM_ENSUREVISIBLE, 0, (LPARAM)TreeView_GetSelection( hwnd ) );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int CALLBACK WINAPI _bcp( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData )
|
|
{
|
|
switch ( uMsg )
|
|
{
|
|
case BFFM_INITIALIZED:
|
|
{
|
|
SetWindowText( hwnd, WASABI_API_LNGSTRINGW( IDS_IMPORT_PLAYLIST_FROM_FOLDER ) );
|
|
SendMessageW( hwnd, BFFM_SETSELECTIONW, 1, (LPARAM)WASABI_API_APP->path_getWorkingPath() );
|
|
|
|
HWND h2 = FindWindowEx( hwnd, NULL, NULL, L"__foo" );
|
|
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;
|
|
}
|
|
|
|
void Playlist_importFromFolders( HWND dlgparent )
|
|
{
|
|
BROWSEINFOW bi = { 0 };
|
|
wchar_t name[ MAX_PATH ] = { 0 };
|
|
|
|
bi.hwndOwner = dlgparent;
|
|
bi.pszDisplayName = name;
|
|
bi.lpszTitle = L"__foo";
|
|
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
|
|
bi.lpfn = _bcp;
|
|
|
|
ITEMIDLIST *idlist = SHBrowseForFolderW( &bi );
|
|
if ( idlist )
|
|
{
|
|
wchar_t path[ MAX_PATH ] = { 0 };
|
|
SHGetPathFromIDListW( idlist, path );
|
|
WASABI_API_APP->path_setWorkingPath( path );
|
|
|
|
Shell_Free( idlist );
|
|
MLNavCtrl_BeginUpdate( plugin.hwndLibraryParent, 0 );
|
|
importPlaylistFolder( path, g_config->ReadInt( L"importplfoldrecurs", 1 ) );
|
|
|
|
AGAVE_API_PLAYLISTS->Flush(); // REVIEW: save immediately? or only at the end?
|
|
MLNavCtrl_EndUpdate( plugin.hwndLibraryParent );
|
|
}
|
|
}
|
|
|
|
static void playlists_Save( HWND parent )
|
|
{
|
|
for ( size_t i = 0; i < playlistGUIDs.size(); i++ )
|
|
{
|
|
if ( !m_playlistslist.GetSelected( i ) )
|
|
continue;
|
|
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
|
|
PlaylistInfo info( playlistGUIDs[ i ] );
|
|
|
|
wchar_t str[ MAX_PATH ] = { 0 };
|
|
if ( PathIsFileSpecW( info.GetFilename() ) )
|
|
PathCombineW( str, g_path, info.GetFilename() );
|
|
else
|
|
lstrcpynW( str, info.GetFilename(), MAX_PATH );
|
|
|
|
Playlist_export( parent, info.GetName(), str );
|
|
}
|
|
}
|
|
|
|
void playlists_Import( HWND hwndDlg, LPARAM lParam )
|
|
{
|
|
RECT r;
|
|
HMENU menu = GetSubMenu( g_context_menus, 2 );
|
|
|
|
GetWindowRect( (HWND)lParam, &r );
|
|
|
|
int x = Menu_TrackPopup( plugin.hwndLibraryParent, menu, TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_BOTTOMALIGN | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RETURNCMD, r.left, r.top, hwndDlg, NULL );
|
|
switch ( x )
|
|
{
|
|
case IDC_IMPORT_PLAYLIST_FROM_FILE:
|
|
Playlist_importFromFile( hwndDlg );
|
|
RefreshPlaylistsList();
|
|
break;
|
|
case IDC_IMPORT_WINAMP_PLAYLIST:
|
|
Playlist_importFromWinamp();
|
|
RefreshPlaylistsList();
|
|
break;
|
|
case ID_PLAYLISTSIMPORT_IMPORTPLAYLISTSFROMFOLDER:
|
|
Playlist_importFromFolders( hwndDlg );
|
|
RefreshPlaylistsList();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void playlists_Add( HWND parent, bool callback )
|
|
{
|
|
WASABI_API_DIALOGBOXPARAMW( ( playlists_CloudAvailable() ? IDD_ADD_CLOUD_PLAYLIST : IDD_ADD_PLAYLIST ), parent, AddPlaylistDialogProc, callback );
|
|
}
|
|
|
|
void DeletePlaylist( GUID _guid, HWND parent, bool confirm )
|
|
{
|
|
wchar_t titleStr[ 32 ] = { 0 };
|
|
if ( confirm && MessageBox( parent, WASABI_API_LNGSTRINGW( IDS_CONFIRM_DELETION ), WASABI_API_LNGSTRINGW_BUF( IDS_CONFIRMATION, titleStr, 32 ), MB_YESNO | MB_ICONQUESTION ) != IDYES )
|
|
{
|
|
SetFocus( parent );
|
|
|
|
return;
|
|
}
|
|
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
PlaylistInfo info( _guid );
|
|
|
|
wchar_t gs[ MAX_PATH + 1 ] = { 0 }, gs2[ MAX_PATH ] = { 0 };
|
|
if ( PathIsFileSpecW( info.GetFilename() ) )
|
|
PathCombineW( gs, g_path, info.GetFilename() );
|
|
else
|
|
lstrcpynW( gs, info.GetFilename(), MAX_PATH );
|
|
|
|
wchar_t l_node[ MAX_PATH ];
|
|
wchar_t l_dir[ MAX_PATH ];
|
|
wchar_t l_fname[ MAX_PATH ];
|
|
wchar_t l_ext[ MAX_PATH ];
|
|
|
|
lstrcpynW( gs2, gs, MAX_PATH );
|
|
_wsplitpath( gs2, l_node, l_dir, l_fname, l_ext );
|
|
_wmakepath( gs2, l_node, l_dir, L"", L"" );
|
|
|
|
AGAVE_API_PLAYLISTS->RemovePlaylist( info.GetIndex() );
|
|
// changed in 5.58 to resolve the issue reported at
|
|
// http://forums.winamp.com/showthread.php?l_plugin_message=2652001#post2652001
|
|
// delete the file after the removal and not before which
|
|
// fixes issues if removing the currently viewed playlist
|
|
//DeleteFileW(gs);
|
|
|
|
// changed in 5.64 to use SHFileOperation(..) instead of DeleteFile(..)
|
|
// so we're able to recover external playlists incase people messup...
|
|
SHFILEOPSTRUCTW fileOp = { 0 };
|
|
fileOp.hwnd = parent;
|
|
fileOp.wFunc = FO_DELETE;
|
|
fileOp.pFrom = gs;
|
|
fileOp.fFlags = ( lstrcmpi( g_path, gs2 ) ? FOF_ALLOWUNDO : 0 ) | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_SIMPLEPROGRESS | FOF_NORECURSION | FOF_NOERRORUI | FOF_SILENT;
|
|
|
|
SHFileOperationW( &fileOp );
|
|
SetFocus( parent );
|
|
}
|
|
|
|
static void playlists_Delete( HWND parent )
|
|
{
|
|
if ( !m_playlistslist.GetSelectedCount() || m_playlistslist.GetSelectionMark() == -1 )
|
|
return;
|
|
|
|
wchar_t titleStr[ 32 ] = { 0 };
|
|
if ( MessageBox( parent, WASABI_API_LNGSTRINGW( IDS_CONFIRM_DELETION ), WASABI_API_LNGSTRINGW_BUF( IDS_CONFIRMATION, titleStr, 32 ), MB_YESNO | MB_ICONQUESTION ) != IDYES )
|
|
return;
|
|
|
|
MLNavCtrl_BeginUpdate( plugin.hwndLibraryParent, 0 );
|
|
for ( int i = playlistGUIDs.size() - 1; i >= 0; i-- )
|
|
{
|
|
if ( !m_playlistslist.GetSelected( i ) )
|
|
continue;
|
|
|
|
DeletePlaylist( playlistGUIDs[ i ], parent, false );
|
|
}
|
|
|
|
AGAVE_API_PLAYLISTS->Flush(); // REVIEW: save immediately? or only at the end?
|
|
MLNavCtrl_EndUpdate( plugin.hwndLibraryParent );
|
|
}
|
|
|
|
static void playlists_Play( int enqueue )
|
|
{
|
|
int deleted = 0;
|
|
for ( size_t i = 0; i < playlistGUIDs.size(); i++ )
|
|
{
|
|
if ( !m_playlistslist.GetSelected( i ) )
|
|
continue;
|
|
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
|
|
PlaylistInfo info( playlistGUIDs[ i ] );
|
|
|
|
wchar_t str[ MAX_PATH ] = { 0 };
|
|
const wchar_t *fn;
|
|
|
|
if ( PathIsFileSpecW( info.GetFilename() ) )
|
|
{
|
|
PathCombineW( str, g_path, info.GetFilename() );
|
|
fn = str;
|
|
}
|
|
else
|
|
{
|
|
fn = info.GetFilename();
|
|
}
|
|
|
|
if ( !enqueue && !deleted )
|
|
{
|
|
SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_DELETE );
|
|
deleted = 1;
|
|
}
|
|
|
|
enqueueFileWithMetaStructW s = { 0 };
|
|
s.filename = fn;
|
|
s.ext = NULL;
|
|
s.length = -1;
|
|
|
|
SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILEW );
|
|
}
|
|
|
|
if ( !enqueue )
|
|
SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_STARTPLAY );
|
|
}
|
|
|
|
static void playlists_ManageButtons( HWND hwndDlg )
|
|
{
|
|
int has_selection = m_playlistslist.GetSelectedCount();
|
|
|
|
const int buttonids[] = { IDC_PLAY, IDC_ENQUEUE, IDC_CUSTOM, IDC_VIEWLIST, IDC_SAVE };
|
|
for ( size_t i = 0; i != sizeof( buttonids ) / sizeof( buttonids[ 0 ] ); i++ )
|
|
{
|
|
HWND controlHWND = GetDlgItem( hwndDlg, buttonids[ i ] );
|
|
EnableWindow( controlHWND, has_selection );
|
|
}
|
|
}
|
|
|
|
static void playlists_ViewList()
|
|
{
|
|
int t = m_playlistslist.GetSelectionMark();
|
|
if ( t >= 0 )
|
|
{
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
|
|
PlaylistInfo info( playlistGUIDs[ t ] );
|
|
//if ( info.treeId == 0 ) // not created yet
|
|
//{
|
|
// // TODO: make a treeid for it
|
|
//}
|
|
mediaLibrary.SelectTreeItem( info.treeId );
|
|
}
|
|
}
|
|
|
|
static void playlists_Paint( HWND hwndDlg )
|
|
{
|
|
int tab[] = { IDC_PLAYLIST_LIST | DCW_SUNKENBORDER, };
|
|
dialogSkinner.Draw( hwndDlg, tab, 1 );
|
|
}
|
|
|
|
LRESULT playlists_cloud_listview( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
if ( uMsg == WM_NOTIFY )
|
|
{
|
|
LPNMHDR l = (LPNMHDR)lParam;
|
|
switch ( l->code )
|
|
{
|
|
case TTN_SHOW:
|
|
{
|
|
LVHITTESTINFO lvh = { 0 };
|
|
GetCursorPos( &lvh.pt );
|
|
ScreenToClient( hwnd, &lvh.pt );
|
|
ListView_SubItemHitTest( hwnd, &lvh );
|
|
|
|
if ( cloud_avail && lvh.iItem != -1 && lvh.iSubItem == 1 )
|
|
{
|
|
LPTOOLTIPTEXTW tt = (LPTOOLTIPTEXTW)lParam;
|
|
RECT r = { 0 };
|
|
if ( lvh.iSubItem )
|
|
ListView_GetSubItemRect( hwnd, lvh.iItem, lvh.iSubItem, LVIR_BOUNDS, &r );
|
|
else
|
|
{
|
|
ListView_GetItemRect( hwnd, lvh.iItem, &r, LVIR_BOUNDS );
|
|
r.right = r.left + ListView_GetColumnWidth( hwnd, 1 );
|
|
}
|
|
|
|
MapWindowPoints( hwnd, HWND_DESKTOP, (LPPOINT)&r, 2 );
|
|
SetWindowPos( tt->hdr.hwndFrom, HWND_TOPMOST, r.right, r.top + 2, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE );
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TTN_NEEDTEXTW:
|
|
{
|
|
LVHITTESTINFO lvh = { 0 };
|
|
GetCursorPos( &lvh.pt );
|
|
ScreenToClient( hwnd, &lvh.pt );
|
|
ListView_SubItemHitTest( hwnd, &lvh );
|
|
|
|
static wchar_t tt_buf1[ 256 ] = { L"" };
|
|
if ( cloud_avail && lvh.iItem != -1 && lvh.iSubItem == 1 )
|
|
{
|
|
LPNMTTDISPINFO lpnmtdi = (LPNMTTDISPINFO)lParam;
|
|
|
|
if ( last_item1 == lvh.iItem )
|
|
{
|
|
lpnmtdi->lpszText = tt_buf1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
if ( lvh.iItem < 0 || lvh.iItem >= (int)playlistGUIDs.size() )
|
|
return 0;
|
|
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
|
|
PlaylistInfo info( playlistGUIDs[ lvh.iItem ] );
|
|
if ( info.Valid() )
|
|
{
|
|
WASABI_API_LNGSTRINGW_BUF( ( !info.GetCloud() ? IDS_UPLOAD_TO_CLOUD : IDS_AVAILABLE_IN_CLOUD ), tt_buf1, ARRAYSIZE( tt_buf1 ) );
|
|
}
|
|
else
|
|
{
|
|
WASABI_API_LNGSTRINGW_BUF( IDS_UPLOAD_TO_CLOUD, tt_buf1, ARRAYSIZE( tt_buf1 ) );
|
|
}
|
|
|
|
last_item1 = lvh.iItem;
|
|
lpnmtdi->lpszText = tt_buf1;
|
|
|
|
// bit of a fiddle but it allows for multi-line tooltips
|
|
//SendMessage(l->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 0);
|
|
}
|
|
else
|
|
return CallWindowProcW( (WNDPROC)GetPropW( hwnd, L"cloud_list_proc" ), hwnd, uMsg, wParam, lParam );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return CallWindowProcW( (WNDPROC)GetPropW( hwnd, L"cloud_list_proc" ), hwnd, uMsg, wParam, lParam );
|
|
}
|
|
|
|
static void playlists_InitDialog( HWND hwndDlg )
|
|
{
|
|
HACCEL accel = WASABI_API_LOADACCELERATORSW( IDR_VIEW_PLS_ACCELERATORS );
|
|
if ( accel )
|
|
WASABI_API_APP->app_addAccelerators( hwndDlg, &accel, 1, TRANSLATE_MODE_CHILD );
|
|
|
|
if ( !view.play )
|
|
SENDMLIPC( plugin.hwndLibraryParent, ML_IPC_GET_VIEW_BUTTON_TEXT, (WPARAM)&view );
|
|
|
|
opened = true;
|
|
loaded = false;
|
|
|
|
cloud_avail = playlists_CloudAvailable();
|
|
groupBtn = g_config->ReadInt( L"groupbtn", 1 );
|
|
enqueuedef = ( g_config->ReadInt( L"enqueuedef", 0 ) == 1 );
|
|
|
|
// v5.66+ - re-use the old predixis parts so the button can be used functionally via ml_enqplay
|
|
// pass the hwnd, button id and plug-in id so the ml plug-in can check things as needed
|
|
pluginMessage l_plugin_message = { ML_MSG_VIEW_BUTTON_HOOK, (INT_PTR)hwndDlg, (INT_PTR)MAKELONG( IDC_CUSTOM, IDC_ENQUEUE ), (INT_PTR)L"ml_playlists_root" };
|
|
wchar_t *pszTextW = (wchar_t *)SENDMLIPC( plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&l_plugin_message );
|
|
if ( pszTextW && pszTextW[ 0 ] != 0 )
|
|
{
|
|
// set this to be a bit different so we can just use one button and not the
|
|
// mixable one as well (leaving that to prevent messing with the resources)
|
|
customAllowed = TRUE;
|
|
SetDlgItemTextW( hwndDlg, IDC_CUSTOM, pszTextW );
|
|
}
|
|
else
|
|
customAllowed = FALSE;
|
|
|
|
/* skin dialog */
|
|
MLSKINWINDOW skinWindow = { 0 };
|
|
skinWindow.skinType = SKINNEDWND_TYPE_DIALOG;
|
|
skinWindow.style = SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT;
|
|
skinWindow.hwndToSkin = hwndDlg;
|
|
|
|
MLSkinWindow( plugin.hwndLibraryParent, &skinWindow );
|
|
|
|
/* skin listview */
|
|
HWND hwndList = skinWindow.hwndToSkin = GetDlgItem( hwndDlg, IDC_PLAYLIST_LIST );
|
|
skinWindow.skinType = SKINNEDWND_TYPE_LISTVIEW;
|
|
skinWindow.style = SWLVS_FULLROWSELECT | SWLVS_DOUBLEBUFFER | SWLVS_ALTERNATEITEMS | SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS;
|
|
|
|
MLSkinWindow( plugin.hwndLibraryParent, &skinWindow );
|
|
MLSkinnedScrollWnd_ShowHorzBar( hwndList, FALSE );
|
|
|
|
/* skin buttons */
|
|
skinWindow.skinType = SKINNEDWND_TYPE_BUTTON;
|
|
skinWindow.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | ( groupBtn ? SWBS_SPLITBUTTON : 0 );
|
|
|
|
const int buttonidz[] = { IDC_PLAY, IDC_ENQUEUE, IDC_CUSTOM };
|
|
for ( size_t i = 0; i != sizeof( buttonidz ) / sizeof( buttonidz[ 0 ] ); i++ )
|
|
{
|
|
skinWindow.hwndToSkin = GetDlgItem( hwndDlg, buttonidz[ i ] );
|
|
if ( IsWindow( skinWindow.hwndToSkin ) )
|
|
MLSkinWindow( plugin.hwndLibraryParent, &skinWindow );
|
|
}
|
|
|
|
/* skin buttons */
|
|
skinWindow.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS;
|
|
|
|
const int buttonids[] = { IDC_VIEWLIST, IDC_CREATENEWPL, IDC_SAVE };
|
|
for ( size_t i = 0; i != sizeof( buttonids ) / sizeof( buttonids[ 0 ] ); i++ )
|
|
{
|
|
skinWindow.hwndToSkin = GetDlgItem( hwndDlg, buttonids[ i ] );
|
|
if ( IsWindow( skinWindow.hwndToSkin ) )
|
|
MLSkinWindow( plugin.hwndLibraryParent, &skinWindow );
|
|
}
|
|
|
|
/* skin dropdown buttons */
|
|
skinWindow.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWBS_DROPDOWNBUTTON;
|
|
skinWindow.hwndToSkin = GetDlgItem( hwndDlg, IDC_IMPORT );
|
|
|
|
MLSkinWindow( plugin.hwndLibraryParent, &skinWindow );
|
|
|
|
|
|
HIMAGELIST imageList = ImageList_Create( 15, 15, ILC_COLOR24, 3, 0 );
|
|
if ( imageList != NULL )
|
|
{
|
|
HIMAGELIST prevList = (HIMAGELIST)SNDMSG( hwndList, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM)imageList );
|
|
if ( prevList != NULL )
|
|
ImageList_Destroy( prevList );
|
|
}
|
|
|
|
m_playlistslist.setwnd( hwndList );
|
|
m_playlistslist.AddCol( WASABI_API_LNGSTRINGW( IDS_PLAYLIST_TITLE ), 400 );
|
|
|
|
int width = 27;
|
|
MLCloudColumn_GetWidth( plugin.hwndLibraryParent, &width );
|
|
m_playlistslist.AddCol( L"", ( cloud_avail ? width : 0 ) );
|
|
|
|
m_playlistslist.AddCol( WASABI_API_LNGSTRINGW( IDS_ITEMS ), 50 );
|
|
m_playlistslist.AutoColumnWidth( 3 );
|
|
m_playlistslist.JustifyColumn( 3, LVCFMT_RIGHT );
|
|
m_playlistslist.AddCol( WASABI_API_LNGSTRINGW( IDS_TIME ), 75 );
|
|
m_playlistslist.AutoSizeColumn( 4 );
|
|
m_playlistslist.JustifyColumn( 4, LVCFMT_RIGHT );
|
|
|
|
MLSkinnedHeader_SetCloudColumn( ListView_GetHeader( hwndList ), ( cloud_avail ? 1 : -1 ) );
|
|
|
|
if ( !GetPropW( hwndList, L"cloud_list_proc" ) )
|
|
SetPropW( hwndList, L"cloud_list_proc", (HANDLE)SetWindowLongPtrW( hwndList, GWLP_WNDPROC, (LONG_PTR)playlists_cloud_listview ) );
|
|
|
|
playlist_UpdateButtonText( hwndDlg, enqueuedef == 1 );
|
|
playlists_ManageButtons( hwndDlg );
|
|
RefreshPlaylistsList();
|
|
|
|
SetWindowRedraw( m_playlistslist.getwnd(), FALSE );
|
|
}
|
|
|
|
void playlists_Destroy( HWND hwndDlg )
|
|
{
|
|
opened = false;
|
|
WASABI_API_APP->app_removeAccelerators( hwndDlg );
|
|
m_playlistslist.setwnd( NULL );
|
|
playlistGUIDs.clear();
|
|
}
|
|
|
|
BOOL playlists_GetDisplayInfo( NMLVDISPINFO *lpdi )
|
|
{
|
|
size_t item = lpdi->item.iItem;
|
|
|
|
if ( item < 0 || item >= playlistGUIDs.size() )
|
|
return 0;
|
|
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
|
|
PlaylistInfo info( playlistGUIDs[ item ] );
|
|
if ( lpdi->item.mask & LVIF_TEXT && info.Valid() )
|
|
{
|
|
switch ( lpdi->item.iSubItem )
|
|
{
|
|
case 0:
|
|
{
|
|
// TODO: this is going to be slow, we should investigate caching the title
|
|
lstrcpyn( lpdi->item.pszText, info.GetName(), lpdi->item.cchTextMax );
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
StringCchPrintf( lpdi->item.pszText, lpdi->item.cchTextMax, L"%d", info.GetCloud() );
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
StringCchPrintf( lpdi->item.pszText, lpdi->item.cchTextMax, L"%d", info.GetSize() );
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
wchar_t str[ 64 ] = { 0 };
|
|
FormatLength( str, info.GetLength(), 64 );
|
|
lstrcpyn( lpdi->item.pszText, str, lpdi->item.cchTextMax );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void Playlists_RenameSelected( HWND hwndDlg )
|
|
{
|
|
// TOOD: loop through selections
|
|
int s = m_playlistslist.GetSelectionMark();
|
|
if ( s != -1 )
|
|
RenamePlaylist( playlistGUIDs[ s ], hwndDlg );
|
|
}
|
|
|
|
BOOL playlists_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 || playlistGUIDs.empty() )
|
|
break;
|
|
|
|
cloudColumnPaint.iItem = plvcd->nmcd.dwItemSpec;
|
|
cloudColumnPaint.iSubItem = plvcd->iSubItem;
|
|
|
|
int cloud_icon = 0;
|
|
size_t item = plvcd->nmcd.dwItemSpec;
|
|
if ( item >= 0 || item < playlistGUIDs.size() )
|
|
{
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
|
|
PlaylistInfo info( playlistGUIDs[ item ] );
|
|
if ( info.Valid() )
|
|
cloud_icon = info.GetCloud();
|
|
}
|
|
|
|
// 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 playlists_Notify( HWND hwndDlg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
LPNMHDR l = (LPNMHDR)lParam;
|
|
if ( l->idFrom == IDC_PLAYLIST_LIST )
|
|
{
|
|
switch ( l->code )
|
|
{
|
|
case LVN_ITEMCHANGED:
|
|
playlists_ManageButtons( hwndDlg );
|
|
break;
|
|
case LVN_BEGINDRAG:
|
|
root_is_drag_and_dropping = 1; SetCapture( hwndDlg );
|
|
break;
|
|
case LVN_GETDISPINFO:
|
|
return playlists_GetDisplayInfo( (NMLVDISPINFO *)lParam );
|
|
case NM_DBLCLK:
|
|
playlists_Play( enqueuedef == 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( pnmitem->hdr.hwndFrom, HWND_DESKTOP, (POINT *)&itemRect, 2 );
|
|
|
|
size_t item = pnmitem->iItem;
|
|
if ( item < 0 || item >= playlistGUIDs.size() )
|
|
return 0;
|
|
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
|
|
PlaylistInfo info( playlistGUIDs[ item ] );
|
|
|
|
HMENU cloud_menu = (HMENU)0x666;
|
|
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 ] );
|
|
|
|
WASABI_API_SYSCB->syscb_issueCallback( api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_GET_CLOUD_STATUS, (intptr_t)uid->string, (intptr_t)&cloud_menu );
|
|
if ( cloud_menu )
|
|
{
|
|
int r = Menu_TrackPopup( plugin.hwndLibraryParent, cloud_menu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_NONOTIFY, itemRect.right, itemRect.top, pnmitem->hdr.hwndFrom, NULL );
|
|
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)info.GetIndex();
|
|
WASABI_API_SYSCB->syscb_issueCallback( api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_PROCESS_CLOUD_STATUS, (intptr_t)r, (intptr_t)&mode );
|
|
if ( mode > 0 )
|
|
{
|
|
info.SetCloud( ( mode == 1 ? 1 : 0 ) );
|
|
AGAVE_API_PLAYLISTS->Flush();
|
|
|
|
UpdatePlaylists();
|
|
|
|
last_item1 = -1;
|
|
}
|
|
}
|
|
|
|
DestroyMenu( cloud_menu );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case LVN_KEYDOWN:
|
|
{
|
|
LPNMLVKEYDOWN pnkd = (LPNMLVKEYDOWN)lParam;
|
|
switch ( pnkd->wVKey )
|
|
{
|
|
case 0x2E: //Delete
|
|
playlists_Delete( hwndDlg );
|
|
break;
|
|
case VK_F2:
|
|
Playlists_RenameSelected( hwndDlg );
|
|
SendMessage( hwndDlg, WM_NEXTDLGCTL, (WPARAM)l->hwndFrom, (LPARAM)TRUE );
|
|
break;
|
|
case 'A':
|
|
if ( !( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) && ( GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) )
|
|
m_playlistslist.SelectAll();
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NM_CUSTOMDRAW:
|
|
{
|
|
LRESULT result = 0;
|
|
if ( cloud_avail && playlists_OnCustomDraw( hwndDlg, (NMLVCUSTOMDRAW *)lParam, &result ) )
|
|
{
|
|
SetWindowLongPtrW( hwndDlg, DWLP_MSGRESULT, (LONG_PTR)result );
|
|
|
|
return 1;
|
|
}
|
|
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;
|
|
}
|
|
|
|
void playlists_MouseMove( HWND hwndDlg, LPARAM lParam )
|
|
{
|
|
if ( root_is_drag_and_dropping && GetCapture() == hwndDlg )
|
|
{
|
|
POINT p = { GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) };
|
|
ClientToScreen( hwndDlg, &p );
|
|
|
|
mlDropItemStruct m = { 0 };
|
|
m.type = ML_TYPE_FILENAMES;
|
|
m.p = p;
|
|
|
|
pluginHandleIpcMessage( ML_IPC_HANDLEDRAG, (WPARAM)&m );
|
|
}
|
|
}
|
|
|
|
void playlists_LeftButtonUp( HWND hwndDlg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
if ( root_is_drag_and_dropping && GetCapture() == hwndDlg )
|
|
{
|
|
ReleaseCapture();
|
|
|
|
POINT p = { GET_X_LPARAM( lParam ),GET_Y_LPARAM( lParam ) };
|
|
ClientToScreen( hwndDlg, &p );
|
|
|
|
mlDropItemStruct m = { 0 };
|
|
m.type = ML_TYPE_FILENAMES;
|
|
m.p = p;
|
|
|
|
pluginHandleIpcMessage( ML_IPC_HANDLEDRAG, (WPARAM)&m );
|
|
|
|
if ( m.result > 0 )
|
|
{
|
|
//std::vector<char> data;
|
|
std::string data;
|
|
|
|
for ( size_t i = 0; i < playlistGUIDs.size(); i++ )
|
|
{
|
|
if ( !m_playlistslist.GetSelected( i ) )
|
|
continue;
|
|
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
|
|
PlaylistInfo info( playlistGUIDs[ i ] );
|
|
|
|
wchar_t str[ MAX_PATH ] = { 0 };
|
|
if ( PathIsFileSpecW( info.GetFilename() ) )
|
|
PathCombineW( str, g_path, info.GetFilename() );
|
|
else
|
|
lstrcpynW( str, info.GetFilename(), MAX_PATH );
|
|
|
|
AutoChar charStr( str );
|
|
// HAKAN: why (len + 1) ?
|
|
//data.append(charStr, lstrlenA( charStr + 1));
|
|
data.append(charStr, lstrlenA(charStr));
|
|
}
|
|
// HAKAN: No need to add trailing zero
|
|
//data.push_back( 0 );
|
|
|
|
m.flags = 0;
|
|
m.result = 0;
|
|
m.data = (void *)data.c_str();
|
|
|
|
pluginHandleIpcMessage( ML_IPC_HANDLEDROP, (WPARAM)&m );
|
|
RefreshPlaylistsList();
|
|
}
|
|
|
|
root_is_drag_and_dropping = 0;
|
|
}
|
|
}
|
|
|
|
enum
|
|
{
|
|
BPM_ECHO_WM_COMMAND = 0x1, // send WM_COMMAND and return value
|
|
BPM_WM_COMMAND = 0x2, // just send WM_COMMAND
|
|
};
|
|
|
|
static void playlists_PlayEnqueue( HWND hwndDlg, HWND from, UINT idFrom )
|
|
{
|
|
HMENU listMenu = GetSubMenu( g_context_menus3, 0 );
|
|
int count = GetMenuItemCount( listMenu );
|
|
if ( count > 2 )
|
|
{
|
|
for ( int i = 2; i < count; i++ )
|
|
DeleteMenu( listMenu, 2, MF_BYPOSITION );
|
|
}
|
|
|
|
UINT menuStatus;
|
|
if ( m_playlistslist.GetNextSelected( -1 ) == -1 )
|
|
menuStatus = MF_BYCOMMAND | MF_GRAYED;
|
|
else
|
|
menuStatus = MF_BYCOMMAND | MF_ENABLED;
|
|
|
|
EnableMenuItem( listMenu, IDC_PLAYLIST_INVERT_SELECTION, menuStatus );
|
|
|
|
if ( m_playlistslist.GetCount() > 0 )
|
|
menuStatus = MF_BYCOMMAND | MF_ENABLED;
|
|
else
|
|
menuStatus = MF_BYCOMMAND | MF_GRAYED;
|
|
|
|
EnableMenuItem( listMenu, IDC_PLAYLIST_SELECT_ALL, menuStatus );
|
|
playlist_ButtonPopupMenu( hwndDlg, idFrom, listMenu, BPM_WM_COMMAND );
|
|
}
|
|
|
|
void playlists_Command( HWND hwndDlg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
switch ( LOWORD( wParam ) )
|
|
{
|
|
case IDC_VIEWLIST:
|
|
playlists_ViewList();
|
|
break;
|
|
case IDC_IMPORT:
|
|
playlists_Import( hwndDlg, lParam );
|
|
break;
|
|
case IDC_CREATENEWPL:
|
|
case IDC_NEWPLAYLIST:
|
|
playlists_Add( hwndDlg );
|
|
break;
|
|
case IDC_PLAY:
|
|
case IDC_ENQUEUE:
|
|
case IDC_CUSTOM:
|
|
{
|
|
if ( HIWORD( wParam ) == MLBN_DROPDOWN )
|
|
{
|
|
playlists_PlayEnqueue( hwndDlg, (HWND)lParam, LOWORD( wParam ) );
|
|
}
|
|
else
|
|
{
|
|
int action;
|
|
if ( LOWORD( wParam ) == IDC_PLAY )
|
|
action = ( HIWORD( wParam ) == 1 ) ? enqueuedef == 1 : 0;
|
|
else if ( LOWORD( wParam ) == IDC_ENQUEUE )
|
|
action = ( HIWORD( wParam ) == 1 ) ? ( enqueuedef != 1 ) : 1;
|
|
else
|
|
// so custom can work with the menu item part
|
|
break;
|
|
|
|
playlists_Play( action );
|
|
}
|
|
break;
|
|
}
|
|
case IDC_SAVE:
|
|
playlists_Save( hwndDlg );
|
|
break;
|
|
case IDC_DELETE:
|
|
playlists_Delete( hwndDlg );
|
|
break;
|
|
case IDC_RENAME:
|
|
Playlists_RenameSelected( hwndDlg );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void playlists_DropFiles( HDROP hDrop )
|
|
{
|
|
wchar_t l_playlist_filename[ 2048 ] = { 0 };
|
|
int y = DragQueryFileW( hDrop, 0xffffffff, l_playlist_filename, 2048 );
|
|
|
|
for ( int x = 0; x < y; x++ )
|
|
{
|
|
Playlist currentPlaylist2;
|
|
DragQueryFileW( hDrop, x, l_playlist_filename, 2048 );
|
|
// make sure that we only add valid playlists and not normal files
|
|
if ( AGAVE_API_PLAYLISTMANAGER->CanLoad( l_playlist_filename ) )
|
|
{
|
|
ImportPlaylist( l_playlist_filename );
|
|
}
|
|
}
|
|
}
|
|
|
|
void playlists_Sort( size_t sort_type )
|
|
{
|
|
int cur_sel = mediaLibrary.GetSelectedTreeItem();
|
|
GUID cur_guid = tree_to_guid_map[ cur_sel ];
|
|
|
|
// keep the old tree ids before sorting so we can then re-map
|
|
// without having to remove and re-add all of the tree items
|
|
std::vector<int> tree_ids;
|
|
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
|
|
size_t count = AGAVE_API_PLAYLISTS->GetCount();
|
|
for ( size_t i = 0; i != count; i++ )
|
|
{
|
|
PlaylistInfo info( i );
|
|
if ( info.Valid() )
|
|
tree_ids.push_back( info.treeId );
|
|
}
|
|
|
|
if ( AGAVE_API_PLAYLISTS->Sort( sort_type ) )
|
|
{
|
|
for ( size_t i = 0; i != count; i++ )
|
|
{
|
|
PlaylistInfo info( i );
|
|
UpdateTree( info, tree_ids[ i ] );
|
|
}
|
|
|
|
for ( size_t i = 0; i != count; i++ )
|
|
{
|
|
PlaylistInfo info( i );
|
|
if ( cur_guid == info.playlist_guid )
|
|
{
|
|
mediaLibrary.SelectTreeItem( info.treeId );
|
|
}
|
|
}
|
|
|
|
RefreshPlaylistsList();
|
|
}
|
|
}
|
|
|
|
void playlists_ContextMenu( HWND hwndDlg, HWND from, int x, int y )
|
|
{
|
|
if ( from != m_playlistslist.getwnd() )
|
|
return;
|
|
|
|
POINT pt = { x,y };
|
|
|
|
if ( x == -1 || y == -1 ) // x and y are -1 if the user invoked a shift-f10 popup menu
|
|
{
|
|
RECT channelRect = { 0 };
|
|
int selected = m_playlistslist.GetNextSelected();
|
|
if ( selected != -1 ) // if something is selected we'll drop the menu from there
|
|
{
|
|
m_playlistslist.GetItemRect( selected, &channelRect );
|
|
ClientToScreen( hwndDlg, (POINT *)&channelRect );
|
|
}
|
|
else // otherwise we'll drop it from the top-left corner of the listview, adjusting for the header location
|
|
{
|
|
GetWindowRect( hwndDlg, &channelRect );
|
|
|
|
HWND hHeader = (HWND)SNDMSG( from, LVM_GETHEADER, 0, 0L );
|
|
RECT headerRect;
|
|
if ( ( WS_VISIBLE & GetWindowLongPtr( hHeader, GWL_STYLE ) ) && GetWindowRect( hHeader, &headerRect ) )
|
|
{
|
|
channelRect.top += ( headerRect.bottom - headerRect.top );
|
|
}
|
|
}
|
|
|
|
x = channelRect.left;
|
|
y = channelRect.top;
|
|
}
|
|
|
|
HWND hHeader = (HWND)SNDMSG( from, LVM_GETHEADER, 0, 0L );
|
|
RECT headerRect;
|
|
if ( 0 == ( WS_VISIBLE & GetWindowLongPtr( hHeader, GWL_STYLE ) ) || FALSE == GetWindowRect( hHeader, &headerRect ) )
|
|
SetRectEmpty( &headerRect );
|
|
|
|
if ( FALSE != PtInRect( &headerRect, pt ) )
|
|
return;
|
|
|
|
HMENU menu = GetSubMenu( g_context_menus, 0 );
|
|
sendTo.AddHere( hwndDlg, GetSubMenu( menu, 2 ), ML_TYPE_FILENAMES, 1, ( ML_TYPE_PLAYLIST + 1 ) );
|
|
|
|
HMENU cloud_hmenu = (HMENU)0x666;
|
|
size_t index = 0, i = 0;
|
|
if ( playlists_CloudAvailable() )
|
|
{
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
for ( ; i < playlistGUIDs.size(); i++ )
|
|
{
|
|
if ( !m_playlistslist.GetSelected( i ) )
|
|
continue;
|
|
|
|
PlaylistInfo info( playlistGUIDs[ i ] );
|
|
|
|
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 );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
UpdateMenuItems( hwndDlg, menu );
|
|
|
|
UINT menuStatus;
|
|
if ( m_playlistslist.GetNextSelected( -1 ) == -1 )
|
|
{
|
|
menuStatus = MF_BYCOMMAND | MF_GRAYED;
|
|
EnableMenuItem( menu, 2, MF_BYPOSITION | MF_GRAYED );
|
|
EnableMenuItem( menu, CLOUD_SOURCE_MENUS, MF_BYCOMMAND | MF_GRAYED );
|
|
}
|
|
else
|
|
{
|
|
menuStatus = MF_BYCOMMAND | MF_ENABLED;
|
|
EnableMenuItem( menu, 2, MF_BYPOSITION | MF_ENABLED );
|
|
EnableMenuItem( menu, CLOUD_SOURCE_MENUS, MF_BYCOMMAND | MF_ENABLED );
|
|
}
|
|
|
|
EnableMenuItem( menu, IDC_PLAY, menuStatus );
|
|
EnableMenuItem( menu, IDC_ENQUEUE, menuStatus );
|
|
EnableMenuItem( menu, IDC_DELETE, menuStatus );
|
|
EnableMenuItem( menu, ID_QUERYMENU_ADDNEWQUERY, menuStatus );
|
|
EnableMenuItem( menu, IDC_RENAME, menuStatus );
|
|
EnableMenuItem( menu, IDC_ENQUEUE, menuStatus );
|
|
EnableMenuItem( menu, IDC_VIEWLIST, menuStatus );
|
|
|
|
int r = Menu_TrackPopup( plugin.hwndLibraryParent, menu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, x, y, hwndDlg, NULL );
|
|
switch ( r )
|
|
{
|
|
case IDC_VIEWLIST:
|
|
playlists_ViewList();
|
|
break;
|
|
case IDC_NEWPLAYLIST:
|
|
playlists_Add( hwndDlg );
|
|
break;
|
|
case IDC_PLAY:
|
|
playlists_Play( 0 );
|
|
break;
|
|
case IDC_ENQUEUE:
|
|
playlists_Play( 1 );
|
|
break;
|
|
case IDC_DELETE:
|
|
playlists_Delete( hwndDlg );
|
|
SendMessage( hwndDlg, WM_NEXTDLGCTL, (WPARAM)from, (LPARAM)TRUE );
|
|
break;
|
|
case ID_QUERYMENU_ADDNEWQUERY:
|
|
playlists_Add( hwndDlg );
|
|
SendMessage( hwndDlg, WM_NEXTDLGCTL, (WPARAM)from, (LPARAM)TRUE );
|
|
break;
|
|
case IDC_RENAME:
|
|
Playlists_RenameSelected( hwndDlg );
|
|
SendMessage( hwndDlg, WM_NEXTDLGCTL, (WPARAM)from, (LPARAM)TRUE );
|
|
break;
|
|
default:
|
|
if ( sendTo.WasClicked( r ) )
|
|
{
|
|
bool playlist_type_worked = true;
|
|
int numPlaylists = m_playlistslist.GetSelectedCount();
|
|
if ( !numPlaylists )
|
|
break;
|
|
|
|
mlPlaylist **playlists = new mlPlaylist * [ numPlaylists + 1 ];
|
|
playlists[ numPlaylists ] = 0; // null terminate
|
|
|
|
// TODO: m_playlistslist.GetNextSelected()
|
|
for ( int i = 0, pl = 0; i < m_playlistslist.GetCount(); i++ )
|
|
{
|
|
if ( !m_playlistslist.GetSelected( i ) )
|
|
continue;
|
|
|
|
playlists[ pl ] = new mlPlaylist;
|
|
memset( playlists[ pl ], 0, sizeof( mlPlaylist ) );
|
|
|
|
{ // scope for lock
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
|
|
PlaylistInfo info( playlistGUIDs[ i ] );
|
|
playlists[ pl ]->filename = _wcsdup( info.GetFilename() );
|
|
playlists[ pl ]->length = info.GetLength();
|
|
playlists[ pl ]->numItems = info.GetSize();
|
|
playlists[ pl ]->title = _wcsdup( info.GetName() );
|
|
}
|
|
|
|
pl++;
|
|
}
|
|
|
|
if ( sendTo.SendPlaylists( playlists ) != 1 )
|
|
{
|
|
for ( int i = 0; i < numPlaylists; i++ )
|
|
{
|
|
if ( sendTo.SendPlaylist( playlists[ i ] ) != 1 )
|
|
{
|
|
playlist_type_worked = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( int i = 0; i < numPlaylists; i++ )
|
|
{
|
|
free( (void *)playlists[ i ]->filename );
|
|
free( (void *)playlists[ i ]->title );
|
|
|
|
delete playlists[ i ];
|
|
}
|
|
|
|
delete[] playlists;
|
|
|
|
|
|
if ( !playlist_type_worked )
|
|
{
|
|
//std::vector<wchar_t> data;
|
|
std::wstring data;
|
|
|
|
{ // scope for lock
|
|
AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
|
|
|
|
for ( size_t ipl = 0; ipl < playlistGUIDs.size(); ipl++ )
|
|
{
|
|
if ( !m_playlistslist.GetSelected( ipl ) )
|
|
continue;
|
|
PlaylistInfo info( playlistGUIDs[ ipl ] );
|
|
|
|
wchar_t str[ MAX_PATH ] = { 0 };
|
|
|
|
if ( PathIsFileSpecW( info.GetFilename() ) )
|
|
PathCombineW( str, g_path, info.GetFilename() );
|
|
else
|
|
lstrcpynW( str, info.GetFilename(), MAX_PATH );
|
|
|
|
// HAKAN: why (len + 1) ?
|
|
//data.append( str, lstrlen(str) + 1);
|
|
data.append(str, lstrlen(str));
|
|
}
|
|
}
|
|
|
|
// HAKAN: No need to add trailing zero
|
|
//data.push_back( 0 );
|
|
//
|
|
// build my data.
|
|
sendTo.SendFilenames( data.c_str() );
|
|
}
|
|
}
|
|
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 )
|
|
{
|
|
PlaylistInfo info( playlistGUIDs[ i ] );
|
|
info.SetCloud( ( mode == 1 ? 1 : 0 ) );
|
|
|
|
AGAVE_API_PLAYLISTS->Flush();
|
|
|
|
UpdatePlaylists();
|
|
|
|
last_item1 = -1;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
sendTo.Cleanup();
|
|
|
|
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 );
|
|
}
|
|
}
|
|
|
|
static HRGN g_rgnUpdate = NULL;
|
|
static int offsetX = 0;
|
|
static int offsetY = 0;
|
|
|
|
typedef struct _LAYOUT
|
|
{
|
|
INT id;
|
|
HWND hwnd;
|
|
INT x;
|
|
INT y;
|
|
INT cx;
|
|
INT cy;
|
|
DWORD flags;
|
|
HRGN rgn;
|
|
}
|
|
LAYOUT, PLAYOUT;
|
|
|
|
#define SETLAYOUTPOS(_layout, _x, _y, _cx, _cy) { _layout->x=_x; _layout->y=_y;_layout->cx=_cx;_layout->cy=_cy;_layout->rgn=NULL; }
|
|
#define SETLAYOUTFLAGS(_layout, _r) \
|
|
{ \
|
|
BOOL fVis; \
|
|
fVis = (WS_VISIBLE & (LONG)GetWindowLongPtr(_layout->hwnd, GWL_STYLE)); \
|
|
if (_layout->x == _r.left && _layout->y == _r.top) _layout->flags |= SWP_NOMOVE; \
|
|
if (_layout->cx == (_r.right - _r.left) && _layout->cy == (_r.bottom - _r.top)) _layout->flags |= SWP_NOSIZE; \
|
|
if ((SWP_HIDEWINDOW & _layout->flags) && !fVis) _layout->flags &= ~SWP_HIDEWINDOW; \
|
|
if ((SWP_SHOWWINDOW & _layout->flags) && fVis) _layout->flags &= ~SWP_SHOWWINDOW; \
|
|
}
|
|
|
|
#define LAYOUTNEEEDUPDATE(_layout) ((SWP_NOMOVE | SWP_NOSIZE) != ((SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW | SWP_SHOWWINDOW) & _layout->flags))
|
|
|
|
#define GROUP_MIN 0x1
|
|
#define GROUP_MAX 0x2
|
|
#define GROUP_STATUSBAR 0x1
|
|
#define GROUP_MAIN 0x2
|
|
|
|
static void LayoutWindows( HWND hwnd, BOOL fRedraw, BOOL fUpdateAll = FALSE )
|
|
{
|
|
static INT controls[] =
|
|
{
|
|
GROUP_STATUSBAR, IDC_PLAY, IDC_ENQUEUE, IDC_CUSTOM, IDC_VIEWLIST, IDC_CREATENEWPL, IDC_IMPORT, IDC_SAVE,
|
|
GROUP_MAIN, IDC_PLAYLIST_LIST
|
|
};
|
|
|
|
INT index;
|
|
RECT rc, rg, ri;
|
|
LAYOUT layout[ sizeof( controls ) / sizeof( controls[ 0 ] ) ], *pl;
|
|
BOOL skipgroup;
|
|
HRGN rgn = NULL;
|
|
|
|
GetClientRect( hwnd, &rc );
|
|
if ( rc.right == rc.left || rc.bottom == rc.top )
|
|
return;
|
|
|
|
if ( rc.right > WASABI_API_APP->getScaleX( 4 ) )
|
|
rc.right -= WASABI_API_APP->getScaleX( 4 );
|
|
|
|
SetRect( &rg, rc.left, rc.top, rc.right, rc.top );
|
|
|
|
pl = layout;
|
|
skipgroup = FALSE;
|
|
|
|
InvalidateRect( hwnd, NULL, TRUE );
|
|
|
|
for ( index = 0; index < sizeof( controls ) / sizeof( *controls ); index++ )
|
|
{
|
|
if ( controls[ index ] >= GROUP_MIN && controls[ index ] <= GROUP_MAX ) // group id
|
|
{
|
|
skipgroup = FALSE;
|
|
switch ( controls[ index ] )
|
|
{
|
|
case GROUP_STATUSBAR:
|
|
{
|
|
wchar_t buffer[ 128 ] = { 0 };
|
|
GetDlgItemTextW( hwnd, IDC_PLAY, buffer, ARRAYSIZE( buffer ) );
|
|
LRESULT idealSize = MLSkinnedButton_GetIdealSize( GetDlgItem( hwnd, IDC_PLAY ), buffer );
|
|
|
|
SetRect( &rg, rc.left + WASABI_API_APP->getScaleX( 1 ),
|
|
rc.bottom - WASABI_API_APP->getScaleY( HIWORD( idealSize ) ),
|
|
rc.right, rc.bottom );
|
|
rc.bottom = rg.top - WASABI_API_APP->getScaleY( 3 );
|
|
break;
|
|
}
|
|
case GROUP_MAIN:
|
|
SetRect( &rg, rc.left + WASABI_API_APP->getScaleX( 1 ), rc.top, rc.right, rc.bottom );
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if ( skipgroup )
|
|
continue;
|
|
|
|
pl->id = controls[ index ];
|
|
pl->hwnd = GetDlgItem( hwnd, pl->id );
|
|
if ( !pl->hwnd )
|
|
continue;
|
|
|
|
GetWindowRect( pl->hwnd, &ri );
|
|
MapWindowPoints( HWND_DESKTOP, hwnd, (LPPOINT)&ri, 2 );
|
|
pl->flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS;
|
|
|
|
switch ( pl->id )
|
|
{
|
|
case IDC_PLAY:
|
|
case IDC_ENQUEUE:
|
|
case IDC_CUSTOM:
|
|
case IDC_VIEWLIST:
|
|
case IDC_CREATENEWPL:
|
|
case IDC_IMPORT:
|
|
case IDC_SAVE:
|
|
if ( IDC_CUSTOM != pl->id || customAllowed )
|
|
{
|
|
if ( groupBtn && ( pl->id == IDC_PLAY ) && ( enqueuedef == 1 ) )
|
|
{
|
|
pl->flags |= SWP_HIDEWINDOW;
|
|
break;
|
|
}
|
|
|
|
if ( groupBtn && ( pl->id == IDC_ENQUEUE ) && ( enqueuedef != 1 ) )
|
|
{
|
|
pl->flags |= SWP_HIDEWINDOW;
|
|
break;
|
|
}
|
|
|
|
if ( groupBtn && ( pl->id == IDC_PLAY || pl->id == IDC_ENQUEUE ) && customAllowed )
|
|
{
|
|
pl->flags |= SWP_HIDEWINDOW;
|
|
break;
|
|
}
|
|
|
|
wchar_t buffer[ 128 ] = { 0 };
|
|
GetWindowText( pl->hwnd, buffer, ARRAYSIZE( buffer ) );
|
|
|
|
LRESULT idealSize = MLSkinnedButton_GetIdealSize( pl->hwnd, buffer );
|
|
LONG width = LOWORD( idealSize ) + ( pl->id != IDC_IMPORT ? WASABI_API_APP->getScaleX( 6 ) : 0 );
|
|
|
|
SETLAYOUTPOS( pl, rg.left, rg.bottom - WASABI_API_APP->getScaleY( HIWORD( idealSize ) ), width, WASABI_API_APP->getScaleY( HIWORD( idealSize ) ) );
|
|
|
|
pl->flags |= ( ( rg.right - rg.left ) > width ) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW;
|
|
if ( SWP_SHOWWINDOW & pl->flags )
|
|
rg.left += ( pl->cx + WASABI_API_APP->getScaleX( 4 ) );
|
|
}
|
|
else
|
|
pl->flags |= SWP_HIDEWINDOW;
|
|
break;
|
|
case IDC_PLAYLIST_LIST:
|
|
pl->flags |= ( rg.top < rg.bottom ) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW;
|
|
SETLAYOUTPOS( pl, rg.left, rg.top + WASABI_API_APP->getScaleY( 1 ), rg.right - rg.left + WASABI_API_APP->getScaleX( 1 ), ( rg.bottom - rg.top ) - WASABI_API_APP->getScaleY( 2 ) );
|
|
break;
|
|
}
|
|
|
|
SETLAYOUTFLAGS( pl, ri );
|
|
if ( LAYOUTNEEEDUPDATE( pl ) )
|
|
{
|
|
if ( SWP_NOSIZE == ( ( SWP_HIDEWINDOW | SWP_SHOWWINDOW | SWP_NOSIZE ) & pl->flags ) && ri.left == ( pl->x + offsetX ) && ri.top == ( pl->y + offsetY ) && IsWindowVisible( pl->hwnd ) )
|
|
{
|
|
SetRect( &ri, pl->x, pl->y, pl->cx + pl->x, pl->y + pl->cy );
|
|
ValidateRect( hwnd, &ri );
|
|
}
|
|
|
|
pl++;
|
|
}
|
|
else if ( ( fRedraw || ( !offsetX && !offsetY ) ) && IsWindowVisible( pl->hwnd ) )
|
|
{
|
|
ValidateRect( hwnd, &ri );
|
|
if ( GetUpdateRect( pl->hwnd, NULL, FALSE ) )
|
|
{
|
|
if ( !rgn )
|
|
rgn = CreateRectRgn( 0, 0, 0, 0 );
|
|
|
|
GetUpdateRgn( pl->hwnd, rgn, FALSE );
|
|
OffsetRgn( rgn, pl->x, pl->y );
|
|
InvalidateRgn( hwnd, rgn, FALSE );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pl != layout )
|
|
{
|
|
LAYOUT *pc;
|
|
HDWP hdwp = BeginDeferWindowPos( (INT)( pl - layout ) );
|
|
for ( pc = layout; pc < pl && hdwp; pc++ )
|
|
hdwp = DeferWindowPos( hdwp, pc->hwnd, NULL, pc->x, pc->y, pc->cx, pc->cy, pc->flags );
|
|
|
|
if ( hdwp )
|
|
EndDeferWindowPos( hdwp );
|
|
|
|
if ( !rgn )
|
|
rgn = CreateRectRgn( 0, 0, 0, 0 );
|
|
|
|
for ( pc = layout; pc < pl && hdwp; pc++ )
|
|
{
|
|
switch ( pc->id )
|
|
{
|
|
case IDC_PLAYLIST_LIST:
|
|
PostMessage( hwnd, WM_APP + 100, 0, 0 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( fRedraw )
|
|
{
|
|
GetUpdateRgn( hwnd, rgn, FALSE );
|
|
for ( pc = layout; pc < pl && hdwp; pc++ )
|
|
{
|
|
if ( pc->rgn )
|
|
{
|
|
OffsetRgn( pc->rgn, pc->x, pc->y );
|
|
CombineRgn( rgn, rgn, pc->rgn, RGN_OR );
|
|
}
|
|
}
|
|
|
|
RedrawWindow( hwnd, NULL, rgn, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASENOW | RDW_ALLCHILDREN );
|
|
}
|
|
|
|
if ( g_rgnUpdate )
|
|
{
|
|
GetUpdateRgn( hwnd, g_rgnUpdate, FALSE );
|
|
for ( pc = layout; pc < pl && hdwp; pc++ )
|
|
{
|
|
if ( pc->rgn )
|
|
{
|
|
OffsetRgn( pc->rgn, pc->x, pc->y );
|
|
CombineRgn( g_rgnUpdate, g_rgnUpdate, pc->rgn, RGN_OR );
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( pc = layout; pc < pl && hdwp; pc++ )
|
|
if ( pc->rgn )
|
|
DeleteObject( pc->rgn );
|
|
}
|
|
|
|
if ( rgn )
|
|
DeleteObject( rgn );
|
|
|
|
ValidateRgn( hwnd, NULL );
|
|
}
|
|
|
|
INT_PTR CALLBACK view_playlistsDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
INT_PTR a = dialogSkinner.Handle( hwndDlg, uMsg, wParam, lParam ); if ( a ) return a;
|
|
switch ( uMsg )
|
|
{
|
|
case WM_INITMENUPOPUP:
|
|
sendTo.InitPopupMenu( wParam );
|
|
return 0;
|
|
case WM_INITDIALOG:
|
|
playlists_InitDialog( hwndDlg );
|
|
return TRUE;
|
|
case WM_NOTIFY:
|
|
return playlists_Notify( hwndDlg, wParam, lParam );
|
|
case WM_MOUSEMOVE:
|
|
playlists_MouseMove( hwndDlg, lParam );
|
|
return 0;
|
|
case WM_LBUTTONUP:
|
|
playlists_LeftButtonUp( hwndDlg, wParam, lParam );
|
|
return 0;
|
|
case WM_COMMAND:
|
|
playlists_Command( hwndDlg, wParam, lParam );
|
|
break;
|
|
case WM_PAINT:
|
|
playlists_Paint( hwndDlg );
|
|
return 0;
|
|
case WM_ERASEBKGND:
|
|
return 1; //handled by WADlg_DrawChildWindowBorders in WM_PAINT
|
|
case WM_DESTROY:
|
|
playlists_Destroy( hwndDlg );
|
|
break;
|
|
case WM_DROPFILES:
|
|
playlists_DropFiles( (HDROP)wParam );
|
|
return 0;
|
|
case WM_CONTEXTMENU:
|
|
playlists_ContextMenu( hwndDlg, (HWND)wParam, GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) );
|
|
return 0;
|
|
case WM_DISPLAYCHANGE:
|
|
LayoutWindows( hwndDlg, TRUE );
|
|
return 0;
|
|
case WM_APP + 104:
|
|
{
|
|
playlist_UpdateButtonText( hwndDlg, wParam );
|
|
LayoutWindows( hwndDlg, TRUE );
|
|
|
|
return 0;
|
|
}
|
|
case WM_APP + 102:
|
|
{
|
|
if ( cloud_avail )
|
|
{
|
|
int width = 27;
|
|
MLCloudColumn_GetWidth( plugin.hwndLibraryParent, &width );
|
|
m_playlistslist.SetColumnWidth( 1, width );
|
|
MLSkinnedHeader_SetCloudColumn( ListView_GetHeader( m_playlistslist.getwnd() ), 1 );
|
|
}
|
|
}
|
|
case WM_APP + 101:
|
|
m_playlistslist.RefreshAll(); UpdateWindow( m_playlistslist.getwnd() );
|
|
case WM_APP + 100:
|
|
AutoSizePlaylistColumns();
|
|
if ( !loaded )
|
|
{
|
|
loaded = true;
|
|
SetWindowRedraw( m_playlistslist.getwnd(), TRUE );
|
|
}
|
|
return 0;
|
|
case WM_WINDOWPOSCHANGED:
|
|
if ( ( SWP_NOSIZE | SWP_NOMOVE ) != ( ( SWP_NOSIZE | SWP_NOMOVE ) & ( (WINDOWPOS *)lParam )->flags ) ||
|
|
( SWP_FRAMECHANGED & ( (WINDOWPOS *)lParam )->flags ) )
|
|
{
|
|
LayoutWindows( hwndDlg, !( SWP_NOREDRAW & ( (WINDOWPOS *)lParam )->flags ) );
|
|
}
|
|
return 0;
|
|
case WM_USER + 0x200:
|
|
SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, 1 ); // yes, we support no - redraw resize
|
|
return TRUE;
|
|
case WM_USER + 0x201:
|
|
offsetX = (short)LOWORD( wParam );
|
|
offsetY = (short)HIWORD( wParam );
|
|
g_rgnUpdate = (HRGN)lParam;
|
|
return TRUE;
|
|
case WM_ML_CHILDIPC:
|
|
{
|
|
if ( lParam == ML_CHILDIPC_DROPITEM && wParam )
|
|
{
|
|
mlDropItemStruct *dis = (mlDropItemStruct *)wParam;
|
|
if ( dis )
|
|
{
|
|
switch ( dis->type )
|
|
{
|
|
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:
|
|
// check we're not dropping back on ourself - not
|
|
// pretty but it prevents the new playlist prompt
|
|
if ( root_is_drag_and_dropping )
|
|
{
|
|
RECT r;
|
|
GetWindowRect( hwndDlg, &r );
|
|
dis->result = ( !PtInRect( &r, dis->p ) ? 1 : -1 );
|
|
}
|
|
// otherwise allow through as from external
|
|
else
|
|
dis->result = 1;
|
|
break;
|
|
default:
|
|
dis->result = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
} |