diff --git a/tools/PsfPlayer/PsfPlayer.vcproj b/tools/PsfPlayer/PsfPlayer.vcproj
index f3188242..44f4a297 100644
--- a/tools/PsfPlayer/PsfPlayer.vcproj
+++ b/tools/PsfPlayer/PsfPlayer.vcproj
@@ -205,6 +205,14 @@
RelativePath=".\Source\HighResTimer.h"
>
+
+
+
+
@@ -320,6 +328,10 @@
RelativePath=".\Source\win32_ui\MiniDebugger.h"
>
+
+
@@ -328,6 +340,14 @@
RelativePath=".\Source\win32_ui\PlayerWnd.h"
>
+
+
+
+
@@ -368,6 +388,10 @@
RelativePath=".\Source\win32_ui\StdAfx.h"
>
+
+
+
+class CPlaylist
+{
+public:
+ struct PLAYLIST_ITEM
+ {
+ std::wstring title;
+ double length;
+ };
+
+ typedef std::map PlaylistItemMap;
+ typedef PlaylistItemMap::const_iterator PlaylistItemMapIterator;
+
+ typedef boost::signal OnItemChangeEvent;
+
+ CPlaylist();
+ virtual ~CPlaylist();
+
+ void AddItem(const char*, const CPsfTags&);
+ bool DoesItemExist(const char*);
+
+ OnItemChangeEvent OnItemChange;
+
+private:
+ PlaylistItemMap m_playlistItems;
+};
+
+#endif
diff --git a/tools/PsfPlayer/Source/PsfTags.cpp b/tools/PsfPlayer/Source/PsfTags.cpp
index c3147a31..0f6d67aa 100644
--- a/tools/PsfPlayer/Source/PsfTags.cpp
+++ b/tools/PsfPlayer/Source/PsfTags.cpp
@@ -32,29 +32,105 @@ void CPsfTags::SetDefaultCharEncoding(const CHAR_ENCODING& encoding)
m_defaultEncoding = encoding;
}
-bool CPsfTags::HasTag(const char* tagName)
+bool CPsfTags::HasTag(const char* tagName) const
{
ConstTagIterator tagIterator = m_tags.find(tagName);
return tagIterator != m_tags.end();
}
-string CPsfTags::GetRawTagValue(const char* tagName)
+string CPsfTags::GetRawTagValue(const char* tagName) const
{
ConstTagIterator tagIterator = m_tags.find(tagName);
if(tagIterator == m_tags.end()) return "";
return tagIterator->second;
}
-wstring CPsfTags::GetTagValue(const char* tagName)
+wstring CPsfTags::GetTagValue(const char* tagName) const
{
return m_stringConverter(GetRawTagValue(tagName));
}
-wstring CPsfTags::DecodeTagValue(const char* value)
+wstring CPsfTags::DecodeTagValue(const char* value) const
{
return m_stringConverter(string(value));
}
+double CPsfTags::ConvertTimeString(const wchar_t* value)
+{
+ double time = 0;
+
+ enum
+ {
+ STATE_CHECK_HAS_SEPARATOR,
+ STATE_CHECK_HAS_OTHER_SEPARATOR,
+ STATE_SPLIT_SECONDS_DECIMAL,
+ STATE_DONE
+ };
+
+ struct FindSeparator
+ {
+ const wchar_t* operator()(const wchar_t* string)
+ {
+ const wchar_t* separator = wcschr(string, ':');
+ if(separator == NULL)
+ {
+ separator = wcschr(string, ';');
+ }
+ return separator;
+ }
+ };
+
+ unsigned int currentState = STATE_CHECK_HAS_SEPARATOR;
+ std::wstring firstUnit;
+
+ while(currentState != STATE_DONE)
+ {
+ if(currentState == STATE_CHECK_HAS_SEPARATOR)
+ {
+ const wchar_t* separator = FindSeparator()(value);
+ if(separator == NULL)
+ {
+ //No colon found -> doesn't have hours or minutes
+ currentState = STATE_SPLIT_SECONDS_DECIMAL;
+ continue;
+ }
+
+ firstUnit = std::wstring(value, separator);
+ currentState = STATE_CHECK_HAS_OTHER_SEPARATOR;
+ value = separator + 1;
+ }
+ else if(currentState == STATE_CHECK_HAS_OTHER_SEPARATOR)
+ {
+ const wchar_t* separator = FindSeparator()(value);
+ std::wstring minutesStr, hoursStr;
+ if(separator == NULL)
+ {
+ //No colon found -> first unit is minutes
+ minutesStr = firstUnit;
+ }
+ else
+ {
+ hoursStr = firstUnit;
+ minutesStr = std::wstring(value, separator);
+ value = separator + 1;
+ }
+ int hours = wcstol(hoursStr.c_str(), NULL, 10);
+ int minutes = wcstol(minutesStr.c_str(), NULL, 10);
+ currentState = STATE_SPLIT_SECONDS_DECIMAL;
+ time += minutes * 60;
+ time += hours * 60 * 60;
+ }
+ else if(currentState == STATE_SPLIT_SECONDS_DECIMAL)
+ {
+ double seconds = wcstod(value, NULL);
+ time += seconds;
+ currentState = STATE_DONE;
+ }
+ }
+
+ return time;
+}
+
CPsfTags::ConstTagIterator CPsfTags::GetTagsBegin() const
{
return m_tags.begin();
diff --git a/tools/PsfPlayer/Source/PsfTags.h b/tools/PsfPlayer/Source/PsfTags.h
index 7a3ac146..fce7944b 100644
--- a/tools/PsfPlayer/Source/PsfTags.h
+++ b/tools/PsfPlayer/Source/PsfTags.h
@@ -22,10 +22,12 @@ public:
CPsfTags(const TagMap&);
virtual ~CPsfTags();
void SetDefaultCharEncoding(const CHAR_ENCODING&);
- bool HasTag(const char*);
- std::string GetRawTagValue(const char*);
- std::wstring GetTagValue(const char*);
- std::wstring DecodeTagValue(const char*);
+ bool HasTag(const char*) const;
+ std::string GetRawTagValue(const char*) const;
+ std::wstring GetTagValue(const char*) const;
+ std::wstring DecodeTagValue(const char*) const;
+
+ static double ConvertTimeString(const wchar_t*);
ConstTagIterator GetTagsBegin() const;
ConstTagIterator GetTagsEnd() const;
diff --git a/tools/PsfPlayer/Source/PsfVm.cpp b/tools/PsfPlayer/Source/PsfVm.cpp
index dd90633f..bce25a3b 100644
--- a/tools/PsfPlayer/Source/PsfVm.cpp
+++ b/tools/PsfPlayer/Source/PsfVm.cpp
@@ -275,7 +275,7 @@ void CPsfVm::ThreadProc()
m_OnRunningStateChange();
}
#else
- if(!m_spuHandler || !m_spuHandler->HasFreeBuffers())
+ if(m_spuHandler && !m_spuHandler->HasFreeBuffers())
{
boost::this_thread::sleep(boost::posix_time::milliseconds(16));
m_spuHandler->RecycleBuffers();
@@ -309,7 +309,10 @@ void CPsfVm::ThreadProc()
currentBlock++;
if(currentBlock == blockCount)
{
- m_spuHandler->Write(samples, blockSize * blockCount, 44100);
+ if(m_spuHandler)
+ {
+ m_spuHandler->Write(samples, blockSize * blockCount, 44100);
+ }
currentBlock = 0;
}
}
diff --git a/tools/PsfPlayer/Source/win32_ui/MainWindow.cpp b/tools/PsfPlayer/Source/win32_ui/MainWindow.cpp
index 470e1c85..5c3d6857 100644
--- a/tools/PsfPlayer/Source/win32_ui/MainWindow.cpp
+++ b/tools/PsfPlayer/Source/win32_ui/MainWindow.cpp
@@ -22,9 +22,9 @@
CMainWindow::SPUHANDLER_INFO CMainWindow::m_handlerInfo[] =
{
- { 1, _T("Win32 WaveOut"), _T("SH_WaveOut.dll") },
- { 2, _T("OpenAL"), _T("SH_OpenAL.dll") },
- { NULL, NULL, NULL },
+ { 1, _T("Win32 WaveOut"), _T("SH_WaveOut.dll") },
+ { 2, _T("OpenAL"), _T("SH_OpenAL.dll") },
+ { NULL, NULL, NULL },
};
using namespace Framework;
@@ -35,19 +35,27 @@ m_virtualMachine(virtualMachine),
m_ready(false),
m_frames(0),
m_writes(0),
+m_selectedAudioHandler(0),
m_ejectButton(NULL),
m_pauseButton(NULL),
-m_aboutButton(NULL)
+m_aboutButton(NULL),
+m_playlistPanel(NULL)
{
- m_selectedAudioHandler = m_handlerInfo[0].id;
+ for(unsigned int i = 0; i < MAX_PANELS; i++)
+ {
+ m_panels[i] = NULL;
+ }
- if(!DoesWindowClassExist(CLSNAME))
+ if(!DoesWindowClassExist(CLSNAME))
{
RegisterClassEx(&Win32::CWindow::MakeWndClass(CLSNAME));
}
// Create(WNDSTYLEEX, CLSNAME, APP_NAME, WNDSTYLE, Win32::CRect(0, 0, 320, 480), NULL, NULL);
- Create(WNDSTYLEEX, CLSNAME, APP_NAME, WNDSTYLE, Win32::CRect(0, 0, 200, 175), NULL, NULL);
+
+ Win32::CRect clientRect(0, 0, 320, 480);
+ AdjustWindowRectEx(clientRect, WNDSTYLE, FALSE, WNDSTYLEEX);
+ Create(WNDSTYLEEX, CLSNAME, APP_NAME, WNDSTYLE, clientRect, NULL, NULL);
SetClassPtr();
SetTimer(m_hWnd, 0, 1000, NULL);
@@ -61,11 +69,13 @@ m_aboutButton(NULL)
m_virtualMachine.OnNewFrame.connect(std::tr1::bind(&CMainWindow::OnNewFrame, this));
m_virtualMachine.OnBufferWrite.connect(std::tr1::bind(&CMainWindow::OnBufferWrite, this, std::tr1::placeholders::_1));
- m_virtualMachine.SetSpuHandler(std::tr1::bind(&CMainWindow::CreateHandler, m_handlerInfo[0].dllName));
+ ChangeAudioPlugin(0);
m_timerLabel = new Win32::CStatic(m_hWnd, _T(""), SS_CENTER);
m_titleLabel = new Win32::CStatic(m_hWnd, _T(""), SS_CENTER | SS_NOPREFIX);
+ m_placeHolder = new Win32::CStatic(m_hWnd, Win32::CRect(0, 0, 1, 1), SS_BLACKRECT);
+
m_nextButton = new Win32::CButton(_T(">>"), m_hWnd, Win32::CRect(0, 0, 1, 1));
m_prevButton = new Win32::CButton(_T("<<"), m_hWnd, Win32::CRect(0, 0, 1, 1));
m_pauseButton = new Win32::CButton(_T("Pause"), m_hWnd, Win32::CRect(0, 0, 1, 1));
@@ -77,7 +87,7 @@ m_aboutButton(NULL)
VerticalLayoutContainer(
LayoutExpression(Win32::CLayoutWindow::CreateTextBoxBehavior(100, 15, m_titleLabel)) +
LayoutExpression(Win32::CLayoutWindow::CreateTextBoxBehavior(100, 15, m_timerLabel)) +
- LayoutExpression(CLayoutStretch::Create()) +
+ LayoutExpression(Win32::CLayoutWindow::CreateCustomBehavior(300, 200, 1, 1, m_placeHolder)) +
HorizontalLayoutContainer(
LayoutExpression(Win32::CLayoutWindow::CreateTextBoxBehavior(100, 30, m_prevButton)) +
LayoutExpression(CLayoutStretch::Create()) +
@@ -92,6 +102,7 @@ m_aboutButton(NULL)
)
);
+ //Create tray icon
Win32::CTrayIcon* trayIcon = m_trayIconServer.Insert();
trayIcon->SetIcon(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MAIN)));
trayIcon->SetTip(_T("PsfPlayer"));
@@ -99,12 +110,21 @@ m_aboutButton(NULL)
m_trayIconServer.RegisterHandler(std::tr1::bind(&CMainWindow::OnTrayIconEvent, this,
std::tr1::placeholders::_1, std::tr1::placeholders::_2));
+ //Create play list panel
+ m_playlistPanel = new CPlaylistPanel(m_placeHolder->m_hWnd, m_playlist);
+ m_playlistPanel->OnItemDblClick.connect(std::tr1::bind(&CMainWindow::OnPlaylistItemDblClick, this, std::tr1::placeholders::_1));
+
+ //Initialize panels
+ m_panels[0] = m_playlistPanel;
+
CreateAudioPluginMenu();
UpdateAudioPluginMenu();
UpdateTimer();
UpdateTitle();
UpdateButtons();
RefreshLayout();
+
+ m_panels[0]->Show(SW_SHOW);
}
CMainWindow::~CMainWindow()
@@ -117,14 +137,42 @@ void CMainWindow::RefreshLayout()
RECT rc = GetClientRect();
m_layout->SetRect(rc.left + 10, rc.top + 10, rc.right - 10, rc.bottom - 10);
m_layout->RefreshGeometry();
+
+ for(unsigned int i = 0; i < MAX_PANELS; i++)
+ {
+ CPanel* panel(m_panels[i]);
+ if(panel != NULL)
+ {
+ panel->RefreshLayout();
+ }
+ }
}
CSoundHandler* CMainWindow::CreateHandler(const TCHAR* libraryPath)
{
typedef CSoundHandler* (*HandlerFactoryFunction)();
- HMODULE module = LoadLibrary(libraryPath);
- HandlerFactoryFunction handlerFactory = reinterpret_cast(GetProcAddress(module, "HandlerFactory"));
- CSoundHandler* result = handlerFactory();
+ CSoundHandler* result = NULL;
+ try
+ {
+ HMODULE module = LoadLibrary(libraryPath);
+ if(module == NULL)
+ {
+ throw std::exception();
+ }
+
+ HandlerFactoryFunction handlerFactory = reinterpret_cast(GetProcAddress(module, "HandlerFactory"));
+ if(handlerFactory == NULL)
+ {
+ throw std::exception();
+ }
+
+ result = handlerFactory();
+ }
+ catch(...)
+ {
+ tstring errorMessage = _T("Couldn't create sound handler present in '") + tstring(libraryPath) + _T("'.");
+ MessageBox(m_hWnd, errorMessage.c_str(), APP_NAME, 16);
+ }
return result;
}
@@ -196,6 +244,11 @@ long CMainWindow::OnSize(unsigned int type, unsigned int width, unsigned int hei
return TRUE;
}
+void CMainWindow::OnPlaylistItemDblClick(const char* path)
+{
+ Load(path);
+}
+
void CMainWindow::OnTrayIconEvent(Win32::CTrayIcon* icon, LPARAM param)
{
switch(param)
@@ -297,7 +350,7 @@ void CMainWindow::ChangeAudioPlugin(unsigned int pluginIdx)
{
SPUHANDLER_INFO* handlerInfo = m_handlerInfo + pluginIdx;
m_selectedAudioHandler = handlerInfo->id;
- m_virtualMachine.SetSpuHandler(std::tr1::bind(&CMainWindow::CreateHandler, handlerInfo->dllName));
+ m_virtualMachine.SetSpuHandler(std::tr1::bind(&CMainWindow::CreateHandler, this, handlerInfo->dllName));
UpdateAudioPluginMenu();
}
@@ -365,6 +418,9 @@ void CMainWindow::Load(const char* path)
}
m_virtualMachine.Resume();
+
+ m_playlist.AddItem(path, m_tags);
+
m_ready = true;
}
catch(const exception& except)
@@ -374,6 +430,7 @@ void CMainWindow::Load(const char* path)
MessageBox(m_hWnd, errorString.c_str(), NULL, 16);
m_ready = false;
}
+
UpdateTitle();
UpdateButtons();
UpdateTimer();
diff --git a/tools/PsfPlayer/Source/win32_ui/MainWindow.h b/tools/PsfPlayer/Source/win32_ui/MainWindow.h
index 899840f1..04e39a06 100644
--- a/tools/PsfPlayer/Source/win32_ui/MainWindow.h
+++ b/tools/PsfPlayer/Source/win32_ui/MainWindow.h
@@ -4,11 +4,14 @@
#include "../PsfVm.h"
#include "../PsfBase.h"
#include "../PsfTags.h"
+#include "../Playlist.h"
#include "win32/Window.h"
#include "win32/Static.h"
#include "win32/Button.h"
#include "win32/Layouts.h"
#include "win32/TrayIconServer.h"
+#include "Panel.h"
+#include "PlaylistPanel.h"
class CMainWindow : public Framework::Win32::CWindow
{
@@ -24,6 +27,11 @@ protected:
long OnTimer();
private:
+ enum
+ {
+ MAX_PANELS = 1,
+ };
+
struct SPUHANDLER_INFO
{
int id;
@@ -31,7 +39,7 @@ private:
const TCHAR* dllName;
};
- static CSoundHandler* CreateHandler(const TCHAR*);
+ CSoundHandler* CreateHandler(const TCHAR*);
void OnNewFrame();
void OnBufferWrite(int);
@@ -41,6 +49,7 @@ private:
void OnAbout();
void Load(const char*);
+ void OnPlaylistItemDblClick(const char*);
void OnTrayIconEvent(Framework::Win32::CTrayIcon*, LPARAM);
void DisplayTrayMenu();
void UpdateTimer();
@@ -55,6 +64,8 @@ private:
Framework::Win32::CStatic* m_titleLabel;
Framework::LayoutObjectPtr m_layout;
+ Framework::Win32::CStatic* m_placeHolder;
+
Framework::Win32::CButton* m_nextButton;
Framework::Win32::CButton* m_prevButton;
Framework::Win32::CButton* m_pauseButton;
@@ -63,10 +74,14 @@ private:
Framework::Win32::CTrayIconServer m_trayIconServer;
+ CPanel* m_panels[MAX_PANELS];
+ CPlaylistPanel* m_playlistPanel;
+
HMENU m_popupMenu;
CPsfVm& m_virtualMachine;
CPsfTags m_tags;
+ CPlaylist m_playlist;
bool m_ready;
uint64 m_frames;
int m_writes;
diff --git a/tools/PsfPlayer/Source/win32_ui/Panel.h b/tools/PsfPlayer/Source/win32_ui/Panel.h
new file mode 100644
index 00000000..6c089bfd
--- /dev/null
+++ b/tools/PsfPlayer/Source/win32_ui/Panel.h
@@ -0,0 +1,16 @@
+#ifndef _PANEL_H_
+#define _PANEL_H_
+
+#include "win32/Window.h"
+
+class CPanel : public Framework::Win32::CWindow
+{
+public:
+ virtual ~CPanel() {}
+ virtual void RefreshLayout() = 0;
+
+private:
+
+};
+
+#endif
diff --git a/tools/PsfPlayer/Source/win32_ui/PlaylistPanel.cpp b/tools/PsfPlayer/Source/win32_ui/PlaylistPanel.cpp
new file mode 100644
index 00000000..c1011cc5
--- /dev/null
+++ b/tools/PsfPlayer/Source/win32_ui/PlaylistPanel.cpp
@@ -0,0 +1,150 @@
+#include "PlaylistPanel.h"
+#include "win32/Rect.h"
+#include "TimeToString.h"
+#include "string_cast.h"
+
+#define CLSNAME _T("PlaylistPanel")
+#define WNDSTYLE (WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CHILD)
+#define WNDSTYLEEX (0)
+#define WM_UPDATEVIS (WM_USER + 1)
+
+using namespace Framework;
+
+CPlaylistPanel::CPlaylistPanel(HWND parentWnd, CPlaylist& playlist)
+: m_playlistView(NULL)
+, m_playlist(playlist)
+, m_nextListViewKey(0)
+{
+ if(!DoesWindowClassExist(CLSNAME))
+ {
+ RegisterClassEx(&Win32::CWindow::MakeWndClass(CLSNAME));
+ }
+
+ Create(WNDSTYLEEX, CLSNAME, _T(""), WNDSTYLE, Win32::CRect(0, 0, 1, 1), parentWnd, NULL);
+ SetClassPtr();
+
+ m_playlistView = new Win32::CListView(m_hWnd, Win32::CRect(0, 0, 1, 1), LVS_REPORT | LVS_SORTASCENDING);
+ m_playlistView->SetExtendedListViewStyle(m_playlistView->GetExtendedListViewStyle() | LVS_EX_FULLROWSELECT);
+
+ CreateColumns();
+
+ m_playlist.OnItemChange.connect(std::tr1::bind(&CPlaylistPanel::OnPlaylistItemChange, this, std::tr1::placeholders::_1));
+
+ m_nextListViewKey = 0;
+}
+
+CPlaylistPanel::~CPlaylistPanel()
+{
+
+}
+
+long CPlaylistPanel::OnNotify(WPARAM wParam, NMHDR* hdr)
+{
+ if(CWindow::IsNotifySource(m_playlistView, hdr))
+ {
+ switch(hdr->code)
+ {
+ case NM_DBLCLK:
+ OnPlaylistViewDblClick(reinterpret_cast(hdr));
+ break;
+ }
+ }
+ return FALSE;
+}
+
+void CPlaylistPanel::OnPlaylistViewDblClick(NMITEMACTIVATE* itemActivate)
+{
+ if(itemActivate->iItem == -1) return;
+ uint32 param = m_playlistView->GetItemData(itemActivate->iItem);
+ ListViewKeyMap::right_const_iterator keyIterator = m_listViewKeys.right.find(param);
+ assert(keyIterator != m_listViewKeys.right.end());
+ if(keyIterator != m_listViewKeys.right.end())
+ {
+ OnItemDblClick(keyIterator->second.c_str());
+ }
+}
+
+void CPlaylistPanel::AddItem(uint32 key, const TCHAR* title, const TCHAR* length)
+{
+ LVITEM item;
+ memset(&item, 0, sizeof(LVITEM));
+ item.pszText = const_cast(title);
+ item.lParam = key;
+ item.mask = LVIF_TEXT | LVIF_PARAM;
+ int itemIdx = m_playlistView->InsertItem(&item);
+
+// m_playlist->SetItemText(itemIndex, 1, TimeToString(length).c_str());
+ m_playlistView->SetItemText(itemIdx, 1, length);
+}
+
+void CPlaylistPanel::ModifyItem(uint32 key, const TCHAR* title, const TCHAR* length)
+{
+ int itemIdx = m_playlistView->FindItemData(key);
+ assert(itemIdx != -1);
+ if(itemIdx == -1) return;
+
+ m_playlistView->SetItemText(itemIdx, 0, title);
+ m_playlistView->SetItemText(itemIdx, 1, length);
+}
+
+void CPlaylistPanel::CreateColumns()
+{
+ LVCOLUMN column;
+ memset(&column, 0, sizeof(LVCOLUMN));
+ column.pszText = _T("Title");
+ column.mask = LVCF_TEXT;
+ m_playlistView->InsertColumn(0, &column);
+
+ memset(&column, 0, sizeof(LVCOLUMN));
+ column.pszText = _T("Length");
+ column.mask = LVCF_TEXT;
+ m_playlistView->InsertColumn(1, &column);
+}
+
+void CPlaylistPanel::OnPlaylistItemChange(const CPlaylist::PlaylistItemMapIterator& itemIterator)
+{
+ const CPlaylist::PLAYLIST_ITEM& item(itemIterator->second);
+
+ std::tstring title, length;
+ title = string_cast(item.title);
+ length = TimeToString(item.length);
+
+ ListViewKeyMap::left_const_iterator keyIterator(m_listViewKeys.left.find(itemIterator->first));
+ if(keyIterator == m_listViewKeys.left.end())
+ {
+ //Element
+ uint32 newKey = ++m_nextListViewKey;
+ m_listViewKeys.insert(ListViewKeyMap::value_type(itemIterator->first, newKey));
+ AddItem(newKey, title.c_str(), length.c_str());
+ }
+ else
+ {
+ uint32 key = keyIterator->second;
+ ModifyItem(key, title.c_str(), length.c_str());
+ }
+}
+
+void CPlaylistPanel::RefreshLayout()
+{
+ //Resize panel
+ {
+ Win32::CRect clientRect(0, 0, 0, 0);
+ ::GetClientRect(GetParent(), clientRect);
+ SetSizePosition(clientRect);
+ }
+
+ //Resize list view
+ {
+ Win32::CRect clientRect(0, 0, 0, 0);
+ GetClientRect(clientRect);
+ m_playlistView->SetSizePosition(clientRect);
+ }
+
+ //Resize columns
+ {
+ RECT clientRect = m_playlistView->GetClientRect();
+
+ m_playlistView->SetColumnWidth(0, 3 * clientRect.right / 4);
+ m_playlistView->SetColumnWidth(1, 1 * clientRect.right / 4);
+ }
+}
diff --git a/tools/PsfPlayer/Source/win32_ui/PlaylistPanel.h b/tools/PsfPlayer/Source/win32_ui/PlaylistPanel.h
new file mode 100644
index 00000000..3996e761
--- /dev/null
+++ b/tools/PsfPlayer/Source/win32_ui/PlaylistPanel.h
@@ -0,0 +1,40 @@
+#ifndef _PLAYLISTPANEL_H_
+#define _PLAYLISTPANEL_H_
+
+#include "Panel.h"
+#include "Playlist.h"
+#include "win32/ListView.h"
+#include
+
+class CPlaylistPanel : public CPanel, public boost::signals::trackable
+{
+public:
+ typedef boost::signal OnItemDblClickEvent;
+
+ CPlaylistPanel(HWND, CPlaylist&);
+ virtual ~CPlaylistPanel();
+
+ void RefreshLayout();
+
+ OnItemDblClickEvent OnItemDblClick;
+
+protected:
+ long OnNotify(WPARAM, NMHDR*);
+
+private:
+ typedef boost::bimap ListViewKeyMap;
+
+ void CreateColumns();
+ void OnPlaylistItemChange(const CPlaylist::PlaylistItemMapIterator&);
+ void AddItem(uint32, const TCHAR*, const TCHAR*);
+ void ModifyItem(uint32, const TCHAR*, const TCHAR*);
+ void OnPlaylistViewDblClick(NMITEMACTIVATE*);
+
+ CPlaylist& m_playlist;
+ Framework::Win32::CListView* m_playlistView;
+ ListViewKeyMap m_listViewKeys;
+
+ uint32 m_nextListViewKey;
+};
+
+#endif
diff --git a/tools/PsfPlayer/Source/win32_ui/TimeToString.h b/tools/PsfPlayer/Source/win32_ui/TimeToString.h
new file mode 100644
index 00000000..f9be3368
--- /dev/null
+++ b/tools/PsfPlayer/Source/win32_ui/TimeToString.h
@@ -0,0 +1,26 @@
+#ifndef _TIMETOSTRING_H_
+#define _TIMETOSTRING_H_
+
+#include
+
+template
+static StringType TimeToString(double time)
+{
+// StringType result, separator;
+ StringType separator;
+ separator += ':';
+ unsigned int secs = static_cast(time) % 60;
+ unsigned int mins = static_cast(time) / 60;
+ std::basic_stringstream result;
+ result << mins << separator;
+ result.width(2);
+ result.fill('0');
+ result << secs;
+// result =
+// boost::lexical_cast(mins) +
+// separator +
+// boost::lexical_cast(secs);
+ return result.str();
+}
+
+#endif