1348 lines
30 KiB
C++
1348 lines
30 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// Plainamp, Open source Winamp core
|
|
//
|
|
// Copyright © 2005 Sebastian Pipping <webmaster@hartwork.org>
|
|
//
|
|
// --> http://www.hartwork.org
|
|
//
|
|
// This source code is released under the GNU General Public License (GPL).
|
|
// See GPL.txt for details. Any non-GPL usage is strictly forbidden.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#include "Playlist.h"
|
|
#include "AddDirectory.h"
|
|
#include "Rebar.h"
|
|
#include "Main.h"
|
|
#include "Status.h"
|
|
#include "Console.h"
|
|
#include "Font.h"
|
|
#include "Playback.h"
|
|
#include "InputPlugin.h"
|
|
#include "Prefs.h"
|
|
#include "Config.h"
|
|
#include "Unicode.h"
|
|
#include "Path.h"
|
|
#include "commdlg.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HWND WindowPlaylist = NULL; // extern
|
|
// WNDPROC WndprocPlaylistBackup = NULL;
|
|
|
|
// int iCurIndex = -1;
|
|
// int iMaxIndex = -1;
|
|
|
|
|
|
|
|
// LRESULT CALLBACK WndprocPlaylist( HWND hwnd, UINT message, WPARAM wp, LPARAM lp );
|
|
void Playlist_SelectSingle( int iIndex );
|
|
|
|
|
|
struct PlaylistEntry
|
|
{
|
|
TCHAR * szFilename;
|
|
// More to come
|
|
};
|
|
|
|
/*
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
int Playlist::GetCurIndex()
|
|
{
|
|
return iCurIndex;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
int Playlist::GetMaxIndex()
|
|
{
|
|
return iMaxIndex;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist::SetCurIndex( int iIndex )
|
|
{
|
|
if( iIndex < 0 || iIndex > iMaxIndex ) return false;
|
|
|
|
iCurIndex = iIndex;
|
|
if( bPlaylistFollow )
|
|
{
|
|
Playlist_SelectSingle( iCurIndex );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist::Create()
|
|
{
|
|
if( WindowPlaylist ) return false;
|
|
|
|
PlaylistView::Create();
|
|
|
|
/*
|
|
RECT ClientMain;
|
|
GetClientRect( WindowMain, &ClientMain );
|
|
|
|
const int iClientHeight = ClientMain.bottom - ClientMain.top;
|
|
const int iClientWidth = ClientMain.right - ClientMain.left;
|
|
const int iPlaylistHeight = iClientHeight - iRebarHeight - iStatusHeight;
|
|
|
|
WindowPlaylist = CreateWindowEx(
|
|
WS_EX_CLIENTEDGE,
|
|
TEXT( "LISTBOX" ),
|
|
NULL,
|
|
WS_VSCROLL |
|
|
LBS_DISABLENOSCROLL |
|
|
LBS_EXTENDEDSEL |
|
|
LBS_HASSTRINGS |
|
|
LBS_NOTIFY |
|
|
LBS_NOINTEGRALHEIGHT |
|
|
WS_CHILD |
|
|
WS_VISIBLE, //
|
|
0,
|
|
iRebarHeight, // + -2,
|
|
iClientWidth,
|
|
iPlaylistHeight,
|
|
WindowMain,
|
|
NULL,
|
|
g_hInstance,
|
|
NULL
|
|
);
|
|
|
|
// Exchange window procedure
|
|
WndprocPlaylistBackup = ( WNDPROC )GetWindowLong( WindowPlaylist, GWL_WNDPROC );
|
|
if( WndprocPlaylistBackup != NULL )
|
|
{
|
|
SetWindowLong( WindowPlaylist, GWL_WNDPROC, ( LONG )WndprocPlaylist );
|
|
}
|
|
*/
|
|
Font::Apply( WindowPlaylist );
|
|
|
|
|
|
TCHAR * szPlaylistMind = new TCHAR[ iHomeDirLen + 12 + 1 ];
|
|
memcpy( szPlaylistMind, szHomeDir, iHomeDirLen * sizeof( TCHAR ) );
|
|
memcpy( szPlaylistMind + iHomeDirLen, TEXT( "Plainamp.m3u" ), 12 * sizeof( TCHAR ) );
|
|
szPlaylistMind[ iHomeDirLen + 12 ] = TEXT( '\0' );
|
|
|
|
Playlist::AppendPlaylistFile( szPlaylistMind );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
inline void Playlist_SelectSingle( int iIndex )
|
|
{
|
|
SendMessage(
|
|
WindowPlaylist,
|
|
LB_SETSEL,
|
|
FALSE,
|
|
-1
|
|
);
|
|
|
|
SendMessage(
|
|
WindowPlaylist,
|
|
LB_SETSEL,
|
|
TRUE,
|
|
iIndex
|
|
);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
inline bool Playlist_IsSelected( int iIndex )
|
|
{
|
|
return ( 0 != SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETSEL,
|
|
( WPARAM )iIndex,
|
|
0
|
|
) );
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
inline bool Playlist_SelectModify( int iIndex, bool bSelected )
|
|
{
|
|
return ( LB_ERR != SendMessage(
|
|
WindowPlaylist,
|
|
LB_SETSEL,
|
|
( WPARAM )( bSelected ? TRUE : FALSE ),
|
|
iIndex
|
|
) );
|
|
}
|
|
|
|
|
|
/*
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist_Remove( int iIndex )
|
|
{
|
|
if( iIndex < 0 || iIndex > iMaxIndex ) return false;
|
|
|
|
// Get entry data
|
|
PlaylistEntry * entry = ( PlaylistEntry * )SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETITEMDATA,
|
|
iIndex,
|
|
0
|
|
);
|
|
|
|
// Free data
|
|
if( entry )
|
|
{
|
|
if( entry->szFilename ) delete [] entry->szFilename;
|
|
delete entry;
|
|
}
|
|
|
|
SendMessage(
|
|
WindowPlaylist,
|
|
LB_DELETESTRING,
|
|
iIndex,
|
|
0
|
|
);
|
|
|
|
if( iIndex < iCurIndex )
|
|
iCurIndex--;
|
|
if( ( iIndex == iCurIndex ) && ( iCurIndex == iMaxIndex ) )
|
|
iCurIndex = iMaxIndex - 1;
|
|
iMaxIndex--;
|
|
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
inline int Playlist_GetCount()
|
|
{
|
|
return ( int )SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETCOUNT,
|
|
0,
|
|
0
|
|
);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
inline int Playlist_GetCaretIndex()
|
|
{
|
|
return ( int )SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETCARETINDEX,
|
|
0,
|
|
0
|
|
);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
inline bool Playlist_SetCaretIndex( int iIndex )
|
|
{
|
|
return ( LB_OKAY == SendMessage(
|
|
WindowPlaylist,
|
|
LB_SETCARETINDEX,
|
|
( WPARAM )iIndex,
|
|
FALSE
|
|
) );
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
inline int Playlist_GetSelCount()
|
|
{
|
|
return ( int )SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETSELCOUNT,
|
|
0,
|
|
0
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
int Playlist_MouseToIndex()
|
|
{
|
|
POINT p;
|
|
GetCursorPos( &p );
|
|
ScreenToClient( WindowPlaylist, &p );
|
|
|
|
int iIndex = ( int )SendMessage(
|
|
WindowPlaylist,
|
|
LB_ITEMFROMPOINT,
|
|
0,
|
|
p.x | ( p.y << 16 )
|
|
);
|
|
|
|
if( ( iIndex < 0 ) || ( iIndex > iMaxIndex ) )
|
|
return -1;
|
|
else
|
|
return iIndex;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist::Clear()
|
|
{
|
|
if( iMaxIndex < 0 ) return false;
|
|
|
|
int iCount = iMaxIndex + 1;
|
|
while( iCount-- )
|
|
{
|
|
Playlist_Remove( iCount );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist::RemoveSelected()
|
|
{
|
|
int iSelCount = Playlist_GetSelCount();
|
|
if( iSelCount < 0 ) return false;
|
|
|
|
// Which items are selected?
|
|
int * sel = new int[ iSelCount ];
|
|
LRESULT lResult = SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETSELITEMS,
|
|
( WPARAM )iSelCount,
|
|
( LPARAM )sel
|
|
);
|
|
|
|
// Remove
|
|
if( lResult > 0 )
|
|
{
|
|
while( lResult-- )
|
|
{
|
|
int iIndex = sel[ lResult ];
|
|
Playlist_Remove( iIndex );
|
|
}
|
|
}
|
|
|
|
delete [] sel;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist::Crop()
|
|
{
|
|
int iAllCount = Playlist_GetCount();
|
|
if( iAllCount < 1 ) return false;
|
|
|
|
int iSelCount = Playlist_GetSelCount();
|
|
if( iSelCount < 0 )
|
|
{
|
|
return false;
|
|
}
|
|
else if( iSelCount == 0 )
|
|
{
|
|
// None selected
|
|
return Clear();
|
|
}
|
|
|
|
// Which items are selected?
|
|
int * sel = new int[ iSelCount ];
|
|
LRESULT lResult = SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETSELITEMS,
|
|
( WPARAM )iSelCount,
|
|
( LPARAM )sel
|
|
);
|
|
|
|
int iLowerEqualIndex = iSelCount - 1;
|
|
for( int i = iAllCount - 1; i >= 0; i-- )
|
|
{
|
|
while( ( sel[ iLowerEqualIndex ] > i ) && ( iLowerEqualIndex > 0 ) )
|
|
{
|
|
iLowerEqualIndex--;
|
|
}
|
|
|
|
if( i != sel[ iLowerEqualIndex ] )
|
|
{
|
|
// Not selected -> remove
|
|
Playlist_Remove( i );
|
|
}
|
|
}
|
|
|
|
delete [] sel;
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist_MoveSel( bool bUpOrDown )
|
|
{
|
|
static bool bMoving = false;
|
|
if( bMoving ) return false;
|
|
|
|
bMoving = true;
|
|
|
|
const int iSelCount = Playlist_GetSelCount();
|
|
if( iSelCount < 0 )
|
|
{
|
|
// No items selected
|
|
bMoving = false;
|
|
return false;
|
|
}
|
|
|
|
// Which items are selected?
|
|
int * sel = new int[ iSelCount ];
|
|
LRESULT lResult = SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETSELITEMS,
|
|
( WPARAM )iSelCount,
|
|
( LPARAM )sel
|
|
);
|
|
|
|
if( lResult <= 0 )
|
|
{
|
|
// Nothing to move
|
|
delete [] sel;
|
|
bMoving = false;
|
|
return false;
|
|
}
|
|
|
|
if( ( bUpOrDown && ( sel[ 0 ] == 0 ) ) ||
|
|
( !bUpOrDown && ( sel[ iSelCount - 1 ] == iMaxIndex ) ) )
|
|
{
|
|
// Cannot move
|
|
delete [] sel;
|
|
bMoving = false;
|
|
return false;
|
|
}
|
|
|
|
const int iOldTop = ( int )SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETTOPINDEX,
|
|
0,
|
|
0
|
|
);
|
|
|
|
// 1 _2_[3][4][5] 6 7 [8] 9
|
|
// --> 1 [3][4][5]_2_ 6 [8]_7_ 9
|
|
|
|
// Redrawing OFF
|
|
// SendMessage( WindowPlaylist, WM_SETREDRAW, ( WPARAM )FALSE, 0 );
|
|
|
|
int i = ( bUpOrDown ? 0 : iSelCount - 1 );
|
|
do
|
|
{
|
|
// Backup the jumper
|
|
PlaylistEntry * entry_old = ( PlaylistEntry * )SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETITEMDATA,
|
|
sel[ i ] + ( bUpOrDown ? -1 : 1 ),
|
|
0
|
|
);
|
|
|
|
do
|
|
{
|
|
// Copy <n> on <n +/- 1>
|
|
PlaylistEntry * entry_new = ( PlaylistEntry * )SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETITEMDATA,
|
|
sel[ i ],
|
|
0
|
|
);
|
|
|
|
int iDest = sel[ i ] + ( bUpOrDown ? -1 : 1 );
|
|
|
|
// Update entry display == delete, insert, set data
|
|
SendMessage(
|
|
WindowPlaylist,
|
|
LB_DELETESTRING,
|
|
iDest,
|
|
0
|
|
);
|
|
iDest = ( int )SendMessage(
|
|
WindowPlaylist,
|
|
LB_INSERTSTRING,
|
|
iDest,
|
|
( LPARAM )entry_new->szFilename
|
|
);
|
|
SendMessage(
|
|
WindowPlaylist,
|
|
LB_SETITEMDATA,
|
|
iDest,
|
|
( LPARAM )entry_new
|
|
);
|
|
|
|
if( sel[ i ] == iCurIndex )
|
|
{
|
|
iCurIndex += ( bUpOrDown ? -1 : 1 );
|
|
}
|
|
|
|
i += ( bUpOrDown ? 1 : -1 );
|
|
} while( bUpOrDown
|
|
?
|
|
( i < iSelCount ) && ( sel[ i - 1 ] + 1 == sel[ i ] )
|
|
:
|
|
( i >= 0 ) && ( sel[ i + 1 ] - 1 == sel[ i ] )
|
|
);
|
|
|
|
// Place the jumper
|
|
int iLast = ( bUpOrDown ? sel[ i - 1 ] : sel[ i + 1 ] );
|
|
|
|
// Update entry display == delete, insert, set data
|
|
SendMessage(
|
|
WindowPlaylist,
|
|
LB_DELETESTRING,
|
|
iLast,
|
|
0
|
|
);
|
|
iLast = ( int )SendMessage(
|
|
WindowPlaylist,
|
|
LB_INSERTSTRING,
|
|
iLast,
|
|
( LPARAM )entry_old->szFilename
|
|
);
|
|
SendMessage(
|
|
WindowPlaylist,
|
|
LB_SETITEMDATA,
|
|
iLast,
|
|
( LPARAM )entry_old
|
|
);
|
|
} while( bUpOrDown
|
|
?
|
|
( i < iSelCount )
|
|
:
|
|
( i >= 0 )
|
|
);
|
|
|
|
// Select new indices (old selection went away on insert/delete
|
|
if( bUpOrDown )
|
|
{
|
|
for( i = 0; i < iSelCount; i++ )
|
|
SendMessage( WindowPlaylist, LB_SETSEL, TRUE, sel[ i ] - 1 );
|
|
}
|
|
else
|
|
{
|
|
for( i = 0; i < iSelCount; i++ )
|
|
SendMessage( WindowPlaylist, LB_SETSEL, TRUE, sel[ i ] + 1 );
|
|
}
|
|
|
|
// Prevent scrolling
|
|
SendMessage(
|
|
WindowPlaylist,
|
|
LB_SETTOPINDEX,
|
|
( WPARAM )iOldTop,
|
|
0
|
|
);
|
|
|
|
// Redrawing ON
|
|
// SendMessage( WindowPlaylist, WM_SETREDRAW, ( WPARAM )TRUE, 0 );
|
|
|
|
|
|
delete [] sel;
|
|
bMoving = false;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
LRESULT CALLBACK WndprocPlaylist( HWND hwnd, UINT message, WPARAM wp, LPARAM lp )
|
|
{
|
|
|
|
static bool bDragging = false;
|
|
static bool bMoveLock = false;
|
|
static int iDragStartY;
|
|
static int iItemHeight = 0x7fffffff;
|
|
|
|
switch( message )
|
|
{
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
if( !bDragging || bMoveLock ) break;
|
|
bMoveLock = true;
|
|
|
|
const int y = HIWORD( lp );
|
|
const int diff = y - iDragStartY;
|
|
if( abs( diff ) > iItemHeight / 2 )
|
|
{
|
|
iDragStartY += ( ( diff > 0 ) ? iItemHeight : -iItemHeight );
|
|
Playlist_MoveSel( diff < 0 );
|
|
}
|
|
|
|
bMoveLock = false;
|
|
break;
|
|
}
|
|
|
|
case WM_LBUTTONDOWN:
|
|
{
|
|
if( GetKeyState( VK_MENU ) >= 0 ) break;
|
|
|
|
// Dragging ON
|
|
iDragStartY = HIWORD( lp );
|
|
iItemHeight = ( int )SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETITEMHEIGHT,
|
|
0,
|
|
0
|
|
);
|
|
bDragging = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_LBUTTONUP:
|
|
// Dragging OFF
|
|
bDragging = false;
|
|
break;
|
|
|
|
case WM_SYSKEYDOWN:
|
|
switch( wp ) // [Alt]+[...]
|
|
{
|
|
case VK_UP:
|
|
Playlist_MoveSel( true );
|
|
break;
|
|
|
|
case VK_DOWN:
|
|
Playlist_MoveSel( false );
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_CHAR:
|
|
case WM_KEYUP:
|
|
// SMALL LETTERS!!!!!!
|
|
switch( wp )
|
|
{
|
|
case 'z':
|
|
case 'y':
|
|
case 'x':
|
|
case 'c':
|
|
case 'v':
|
|
case 'b':
|
|
case 'l':
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
{
|
|
const bool bShift = ( ( GetKeyState( VK_SHIFT ) & 0x8000 ) != 0 );
|
|
const bool bControl = ( ( GetKeyState( VK_CONTROL ) & 0x8000 ) != 0 );
|
|
// const bool bAlt = ( ( GetKeyState( VK_MENU ) & 0x8000 ) != 0 );
|
|
|
|
|
|
switch( wp )
|
|
{
|
|
case VK_LEFT:
|
|
if( bShift || bControl ) return 0;
|
|
SendMessage( WindowMain, WM_COMMAND, WINAMP_REW5S, 0 );
|
|
return 0;
|
|
|
|
case VK_RIGHT:
|
|
if( bShift || bControl ) return 0;
|
|
SendMessage( WindowMain, WM_COMMAND, WINAMP_FFWD5S, 0 );
|
|
return 0;
|
|
|
|
case VK_UP:
|
|
if( bControl && !bShift )
|
|
{
|
|
// Move caret not selection
|
|
const int iCaretBefore = Playlist_GetCaretIndex();
|
|
if( iCaretBefore > 0 )
|
|
{
|
|
Playlist_SetCaretIndex( iCaretBefore - 1 );
|
|
}
|
|
else if( ( iCaretBefore == 0 ) && bInfinitePlaylist )
|
|
{
|
|
Playlist_SetCaretIndex( iMaxIndex );
|
|
}
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
if( bInfinitePlaylist )
|
|
{
|
|
if( Playlist_GetCaretIndex() != 0 ) break;
|
|
Playlist_SelectSingle( iMaxIndex );
|
|
return 0; // Or it will increase one more
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_DOWN:
|
|
if( bControl && !bShift )
|
|
{
|
|
// Move caret not selection
|
|
const int iCaretBefore = Playlist_GetCaretIndex();
|
|
if( ( iCaretBefore < iMaxIndex ) && ( iCaretBefore >= 0 ) )
|
|
{
|
|
Playlist_SetCaretIndex( iCaretBefore + 1 );
|
|
}
|
|
else if( ( iCaretBefore == iMaxIndex ) && bInfinitePlaylist )
|
|
{
|
|
Playlist_SetCaretIndex( 0 );
|
|
}
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
if( bInfinitePlaylist )
|
|
{
|
|
if( Playlist_GetCaretIndex() != iMaxIndex ) break;
|
|
Playlist_SelectSingle( 0 );
|
|
return 0; // Or it will increase one more
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_SPACE:
|
|
if( bControl && !bShift )
|
|
{
|
|
const int iCaret = Playlist_GetCaretIndex();
|
|
if( iCaret == -1 ) return 0;
|
|
bool bSelected = Playlist_IsSelected( iCaret );
|
|
Playlist_SelectModify( iCaret, !bSelected );
|
|
}
|
|
return 0;
|
|
|
|
case VK_DELETE:
|
|
{
|
|
if( bShift ) break;
|
|
|
|
if( bControl )
|
|
playlist->RemoveSelected( false );
|
|
else
|
|
playlist->RemoveSelected( true );
|
|
|
|
break;
|
|
}
|
|
|
|
case VK_RETURN:
|
|
iCurIndex = Playlist_GetCaretIndex();
|
|
SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON2, 0 );
|
|
return 0;
|
|
|
|
case 'Y':
|
|
case 'Z':
|
|
if( bShift || bControl ) return 0;
|
|
SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON1, 0 );
|
|
return 0;
|
|
|
|
case 'X':
|
|
if( bShift || bControl ) return 0;
|
|
SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON2, 0 );
|
|
return 0;
|
|
|
|
case 'C':
|
|
if( bShift || bControl ) return 0;
|
|
SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON3, 0 );
|
|
return 0;
|
|
|
|
case 'V':
|
|
// Todo modifiers pressed? -> fadeout/...
|
|
if( bShift || bControl ) return 0;
|
|
SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON4, 0 );
|
|
return 0;
|
|
|
|
case 'B':
|
|
if( bShift || bControl ) return 0;
|
|
SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON5, 0 );
|
|
return 0;
|
|
/
|
|
case 'J':
|
|
if( bShift || bControl ) return 0;
|
|
SendMessage( WindowMain, WM_COMMAND, WINAMP_JUMPFILE, 0 );
|
|
return 0;
|
|
/
|
|
case 'L':
|
|
if( bControl ) return 0;
|
|
SendMessage( WindowMain, WM_COMMAND, bShift ? WINAMP_FILE_DIR : WINAMP_FILE_PLAY, 0 );
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
iCurIndex = Playlist_MouseToIndex();
|
|
if( iCurIndex < 0 ) break;
|
|
Playback::Play();
|
|
Playback::UpdateSeek();
|
|
break;
|
|
|
|
}
|
|
return CallWindowProc( WndprocPlaylistBackup, hwnd, message, wp, lp );
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist::Add( int iIndex, TCHAR * szDisplay, TCHAR * szFilename )
|
|
{
|
|
iMaxIndex++;
|
|
if( iIndex < 0 || iIndex > iMaxIndex ) iIndex = iMaxIndex;
|
|
if( iIndex <= iCurIndex )
|
|
{
|
|
iCurIndex++;
|
|
}
|
|
|
|
// Create entry data
|
|
PlaylistEntry * new_entry = new PlaylistEntry;
|
|
new_entry->szFilename = szFilename;
|
|
|
|
iIndex = ( int )SendMessage(
|
|
WindowPlaylist,
|
|
LB_INSERTSTRING, // LB_ADDSTRING,
|
|
iIndex,
|
|
( LPARAM )szDisplay
|
|
);
|
|
|
|
// Associate data
|
|
SendMessage(
|
|
WindowPlaylist,
|
|
LB_SETITEMDATA,
|
|
iIndex,
|
|
( LPARAM )new_entry
|
|
);
|
|
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// Opens a dialog box and loads the playlist if <bOpenOrSave> is [true],
|
|
/// or saves the playlist if it is [false].
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist_DialogBoth( bool bOpenOrSave )
|
|
{
|
|
TCHAR szFilters[] = TEXT(
|
|
"All files (*.*)\0*.*\0"
|
|
"Playlist files (*.M3U)\0*.m3u\0"
|
|
"\0"
|
|
);
|
|
TCHAR szFilename[ MAX_PATH ] = TEXT( "\0" );
|
|
|
|
OPENFILENAME ofn;
|
|
memset( &ofn, 0, sizeof( OPENFILENAME ) );
|
|
ofn.lStructSize = sizeof( OPENFILENAME );
|
|
ofn.hwndOwner = WindowMain;
|
|
ofn.hInstance = g_hInstance;
|
|
ofn.lpstrFilter = szFilters;
|
|
ofn.lpstrCustomFilter = NULL;
|
|
ofn.nMaxCustFilter = 0;
|
|
ofn.nFilterIndex = 2;
|
|
ofn.lpstrFile = szFilename;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.Flags = OFN_EXPLORER |
|
|
OFN_ENABLESIZING |
|
|
( bOpenOrSave ? OFN_FILEMUSTEXIST : OFN_OVERWRITEPROMPT ) |
|
|
OFN_PATHMUSTEXIST |
|
|
OFN_HIDEREADONLY;
|
|
ofn.nMaxFileTitle = 0,
|
|
ofn.lpstrInitialDir = NULL;
|
|
ofn.lpstrTitle = ( bOpenOrSave ? TEXT( "Open playlist" ) : TEXT( "Save playlist" ) );
|
|
|
|
if( bOpenOrSave )
|
|
{
|
|
if( !GetOpenFileName( &ofn ) ) return false;
|
|
}
|
|
else
|
|
{
|
|
if( !GetSaveFileName( &ofn ) ) return false;
|
|
}
|
|
|
|
if( bOpenOrSave )
|
|
{
|
|
// Open
|
|
const int iFilenameLen = ( int )_tcslen( szFilename );
|
|
if( !_tcsncmp( szFilename + iFilenameLen - 3, TEXT( "m3u" ), 3 ) )
|
|
{
|
|
// Playlist file
|
|
playlist->RemoveAll();
|
|
Playlist::AppendPlaylistFile( szFilename );
|
|
Playback::Play();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO: Check extension, ask for appending if missing
|
|
|
|
// Save
|
|
Playlist::ExportPlaylistFile( szFilename );
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// Opens a dialog box and loads the selected playlist
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist::DialogOpen()
|
|
{
|
|
return Playlist_DialogBoth( true );
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// Opens a dialog box and saves the playlist to the filename selected
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist::DialogSaveAs()
|
|
{
|
|
return Playlist_DialogBoth( false );
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist::AppendPlaylistFile( TCHAR * szFilename )
|
|
{
|
|
// Open playlist file
|
|
HANDLE hFile = CreateFile(
|
|
szFilename, // LPCTSTR lpFileName
|
|
FILE_READ_DATA, // DWORD dwDesiredAccess
|
|
FILE_SHARE_READ, // DWORD dwShareMode
|
|
NULL, // LPSECURITY_ATTRIBUTES lpSecurityAttributes
|
|
OPEN_EXISTING, // DWORD dwCreationDisposition
|
|
FILE_ATTRIBUTE_NORMAL, // DWORD dwFlagsAndAttributes
|
|
NULL // HANDLE hTemplateFile
|
|
);
|
|
|
|
if( hFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
// MessageBox( 0, TEXT( "Could not read playlist file" ), TEXT( "Error" ), MB_ICONERROR );
|
|
return false;
|
|
}
|
|
|
|
const bool bEmptyBefore = ( playlist->GetSize() == 0 );
|
|
|
|
|
|
// Remove filename from <szFilename> so we can
|
|
// use it as relative directory root
|
|
TCHAR * szWalk = szFilename + _tcslen( szFilename ) - 1;
|
|
while( ( szWalk > szFilename ) && ( *szWalk != TEXT( '\\' ) ) ) szWalk--;
|
|
szWalk++;
|
|
*szWalk = TEXT( '\0' );
|
|
|
|
TCHAR * szBaseDir = szFilename;
|
|
const int iBaseDirLen = ( int )_tcslen( szBaseDir );
|
|
|
|
|
|
DWORD iSizeBytes = GetFileSize( hFile, NULL );
|
|
if( iSizeBytes <= 0 )
|
|
{
|
|
CloseHandle( hFile );
|
|
return false;
|
|
}
|
|
|
|
// Allocate
|
|
char * rawdata = new char[ iSizeBytes + 1 ]; // One more so we can write '\0' on EOF
|
|
DWORD iBytesRead;
|
|
|
|
// Read whole file
|
|
ReadFile(
|
|
hFile, // HANDLE hFile
|
|
rawdata, // LPVOID lpBuffer
|
|
iSizeBytes, // DWORD nNumberOfBytesToRead
|
|
&iBytesRead, // LPDWORD lpNumberOfBytesRead
|
|
NULL // LPOVERLAPPED lpOverlapped
|
|
);
|
|
|
|
if( iBytesRead < iSizeBytes )
|
|
{
|
|
delete [] rawdata;
|
|
CloseHandle( hFile );
|
|
|
|
MessageBox( 0, TEXT( "Could not read whole file" ), TEXT( "Error" ), MB_ICONERROR );
|
|
return false;
|
|
}
|
|
|
|
// Parse file content
|
|
|
|
// File must be
|
|
// * M3U
|
|
// * ANSI
|
|
|
|
char * walk = rawdata;
|
|
const char * eof = rawdata + iSizeBytes;
|
|
|
|
char * beg = rawdata;
|
|
char * end;
|
|
|
|
while( true )
|
|
{
|
|
// Find newline or eof
|
|
while( ( walk < eof ) && ( *walk != '\015' ) && ( *walk != '\012' ) ) walk++;
|
|
end = walk;
|
|
|
|
if( ( end - beg > 2 ) && ( *beg != '#' ) )
|
|
{
|
|
TCHAR * szKeep;
|
|
if( beg[ 1 ] == ':' )
|
|
{
|
|
// TODO: Better detection, network path?
|
|
|
|
// Absolute path
|
|
const int iLen = end - beg;
|
|
szKeep = new TCHAR[ iLen + 1 ];
|
|
ToTchar( szKeep, beg, iLen );
|
|
szKeep[ iLen ] = TEXT( '\0' );
|
|
}
|
|
else
|
|
{
|
|
// Skip initial so we don't get a double backslash in between
|
|
while( ( beg[ 0 ] == '\\' ) && ( beg < end ) ) beg++;
|
|
|
|
// Relative path
|
|
const int iSecondLen = end - beg;
|
|
szKeep = new TCHAR[ iBaseDirLen + iSecondLen + 1 ];
|
|
memcpy( szKeep, szBaseDir, iBaseDirLen * sizeof( TCHAR ) );
|
|
ToTchar( szKeep + iBaseDirLen, beg, iSecondLen );
|
|
|
|
szKeep[ iBaseDirLen + iSecondLen ] = TEXT( '\0' );
|
|
|
|
UnbloatFilename( szKeep, false );
|
|
}
|
|
|
|
// if( !Add( iMaxIndex + 1, szKeep, szKeep ) ) break;
|
|
playlist->PushBack( szKeep );
|
|
}
|
|
|
|
// Skip newlines
|
|
while( ( walk < eof ) && ( ( *walk == '\015' ) || ( *walk == '\012' ) ) ) walk++;
|
|
if( walk == eof )
|
|
{
|
|
break;
|
|
}
|
|
|
|
beg = walk;
|
|
}
|
|
|
|
delete [] rawdata;
|
|
CloseHandle( hFile );
|
|
/*
|
|
if( bEmptyBefore )
|
|
{
|
|
iCurIndex = 0;
|
|
}
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist::ExportPlaylistFile( TCHAR * szFilename )
|
|
{
|
|
// Open playlist file
|
|
HANDLE hFile = CreateFile(
|
|
szFilename, // LPCTSTR lpFileName
|
|
FILE_WRITE_DATA, // DWORD dwDesiredAccess
|
|
0, // DWORD dwShareMode
|
|
NULL, // LPSECURITY_ATTRIBUTES lpSecurityAttributes
|
|
CREATE_ALWAYS, // DWORD dwCreationDisposition
|
|
FILE_ATTRIBUTE_NORMAL, // DWORD dwFlagsAndAttributes
|
|
NULL // HANDLE hTemplateFile
|
|
);
|
|
|
|
if( hFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
MessageBox( 0, TEXT( "Could not write playlist file" ), TEXT( "Error" ), MB_ICONERROR );
|
|
return false;
|
|
}
|
|
|
|
|
|
// Remove filename from <szFilename> so we can
|
|
// use it as relative directory root
|
|
TCHAR * szWalk = szFilename + _tcslen( szFilename ) - 1;
|
|
while( ( szWalk > szFilename ) && ( *szWalk != TEXT( '\\' ) ) ) szWalk--;
|
|
szWalk++;
|
|
*szWalk = TEXT( '\0' );
|
|
|
|
TCHAR * szBaseDir = szFilename;
|
|
const int iBaseDirLen = ( int )_tcslen( szBaseDir );
|
|
|
|
|
|
char * rawdata = new char[ ( playlist->GetMaxIndex() + 1 ) * ( MAX_PATH + 2 ) ];
|
|
char * walk = rawdata;
|
|
|
|
// Write playlist to buffer
|
|
const int iMaxMax = playlist->GetMaxIndex();
|
|
for( int i = 0; i <= iMaxMax; i++ )
|
|
{
|
|
// Get
|
|
TCHAR * szEntry = GetFilename( i );
|
|
if( !szEntry ) break;
|
|
int iEntryLen = ( int )_tcslen( szEntry );
|
|
|
|
// Copy
|
|
TCHAR * szTemp = new TCHAR[ iEntryLen + 1 ];
|
|
memcpy( szTemp, szEntry, iEntryLen * sizeof( TCHAR ) );
|
|
szTemp[ iEntryLen ] = TEXT( '\0' );
|
|
|
|
// Convert
|
|
if( ApplyRootToFilename( szBaseDir, szTemp ) )
|
|
{
|
|
// Update length or we are writing too much
|
|
iEntryLen = ( int )_tcslen( szTemp );
|
|
}
|
|
|
|
// Copy
|
|
#ifdef PA_UNICODE
|
|
ToAnsi( walk, szTemp, iEntryLen );
|
|
#else
|
|
memcpy( walk, szTemp, iEntryLen );
|
|
#endif
|
|
|
|
delete [] szTemp;
|
|
|
|
walk += iEntryLen;
|
|
memcpy( walk, "\015\012", 2 );
|
|
walk += 2;
|
|
}
|
|
|
|
const DWORD iSizeBytes = walk - rawdata;
|
|
DWORD iBytesRead;
|
|
WriteFile(
|
|
hFile, // HANDLE hFile,
|
|
rawdata, // LPCVOID lpBuffer,
|
|
iSizeBytes, // DWORD nNumberOfBytesToWrite,
|
|
&iBytesRead, // LPDWORD lpNumberOfBytesWritten,
|
|
NULL // LPOVERLAPPED lpOverlapped
|
|
);
|
|
|
|
if( iBytesRead < iSizeBytes )
|
|
{
|
|
delete [] rawdata;
|
|
CloseHandle( hFile );
|
|
|
|
MessageBox( 0, TEXT( "Could not write whole file" ), TEXT( "Error" ), MB_ICONERROR );
|
|
return false;
|
|
}
|
|
|
|
delete [] rawdata;
|
|
CloseHandle( hFile );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist::Append( TCHAR * szDisplay, TCHAR * szFilename )
|
|
{
|
|
return Add( iMaxIndex + 1, szDisplay, szFilename );
|
|
}
|
|
*/
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
TCHAR * Playlist::GetFilename( int iIndex )
|
|
{
|
|
// if( iIndex < 0 || iIndex > iMaxIndex ) return NULL;
|
|
|
|
/*
|
|
PlaylistEntry * entry = ( PlaylistEntry * )SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETITEMDATA,
|
|
iIndex,
|
|
0
|
|
);
|
|
|
|
return ( entry ? entry->szFilename : NULL );
|
|
*/
|
|
|
|
return ( TCHAR * )playlist->Get( iIndex );
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
int Playlist::GetFilename( int iIndex, char * szAnsiFilename, int iChars )
|
|
{
|
|
// if( iIndex < 0 || iIndex > iMaxIndex ) return 0;
|
|
/*
|
|
PlaylistEntry * entry = ( PlaylistEntry * )SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETITEMDATA,
|
|
iIndex,
|
|
0
|
|
);
|
|
if( !entry || !entry->szFilename ) return 0;
|
|
|
|
TCHAR * & szFilename = entry->szFilename;
|
|
*/
|
|
TCHAR * szFilename = ( TCHAR * )playlist->Get( iIndex );
|
|
|
|
const int iFilenameLen = ( int )_tcslen( szFilename );
|
|
const int iCopyLen = ( iFilenameLen < iChars ) ? iFilenameLen : iChars;
|
|
|
|
#ifdef PA_UNICODE
|
|
ToAnsi( szAnsiFilename, szFilename, iCopyLen );
|
|
#else
|
|
memcpy( szAnsiFilename, szFilename, iCopyLen );
|
|
#endif
|
|
|
|
szAnsiFilename[ iCopyLen ] = '\0';
|
|
return iCopyLen;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
int Playlist::GetTitle( int iIndex, char * szAnsiTitle, int iChars )
|
|
{
|
|
// if( iIndex < 0 || iIndex > iMaxIndex ) return 0;
|
|
/*
|
|
TCHAR * szFilename = ( TCHAR * )SendMessage(
|
|
WindowPlaylist,
|
|
LB_GETITEMDATA,
|
|
iIndex,
|
|
0
|
|
);
|
|
if( !szFilename ) return 0;
|
|
*/
|
|
TCHAR * szFilename = ( TCHAR * )playlist->Get( iIndex );
|
|
|
|
// Get extension
|
|
const int iFilenameLen = ( int )_tcslen( szFilename );
|
|
TCHAR * szExt = szFilename + iFilenameLen - 1;
|
|
while( ( szExt > szFilename ) && ( *szExt != TEXT( '.' ) ) ) szExt--;
|
|
szExt++;
|
|
|
|
// Get plugin for extension
|
|
map <TCHAR *, InputPlugin *, TextCompare>::iterator iter = ext_map.find( szExt );
|
|
if( iter == ext_map.end() ) return 0;
|
|
InputPlugin * input_plugin = iter->second;
|
|
|
|
#ifdef PA_UNICODE
|
|
// Filename
|
|
char * szTemp = new char[ iFilenameLen + 1 ];
|
|
ToAnsi( szTemp, szFilename, iFilenameLen );
|
|
szTemp[ iFilenameLen ] = '\0';
|
|
|
|
// Ansi Title
|
|
char szTitle[ 2000 ] = "\0";
|
|
int length_in_ms;
|
|
input_plugin->plugin->GetFileInfo( szTemp, szTitle, &length_in_ms );
|
|
const int iTitleLen = strlen( szTitle );
|
|
memcpy( szAnsiTitle, szTitle, iChars * sizeof( char ) );
|
|
szTitle[ iChars ] = '\0';
|
|
#else
|
|
char szTitle[ 2000 ] = "\0";
|
|
int length_in_ms;
|
|
input_plugin->plugin->GetFileInfo( szFilename, szTitle, &length_in_ms );
|
|
const int iTitleLen = ( int )strlen( szAnsiTitle );
|
|
memcpy( szAnsiTitle, szTitle, iChars * sizeof( char ) );
|
|
szTitle[ iChars ] = '\0';
|
|
#endif
|
|
|
|
return ( iTitleLen < iChars ) ? iTitleLen : iChars;
|
|
}
|
|
|
|
|
|
/*
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist::SelectZero()
|
|
{
|
|
SendMessage(
|
|
WindowPlaylist,
|
|
LB_SETSEL,
|
|
FALSE,
|
|
-1
|
|
);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool Playlist::SelectAll()
|
|
{
|
|
SendMessage(
|
|
WindowPlaylist,
|
|
LB_SETSEL,
|
|
TRUE,
|
|
-1
|
|
);
|
|
return true;
|
|
}
|
|
*/
|