windows-nt/Source/XPSP1/NT/shell/ext/zaxxon/zaxxoneditor.cpp
2020-09-26 16:20:57 +08:00

593 lines
16 KiB
C++

#include "priv.h"
#include "zaxxon.h"
#include "guids.h"
#include "shlwapip.h"
#include "resource.h"
#include "commdlg.h"
#include "varutil.h"
#define ID_TOOLBAR 100
#define ID_LISTVIEW 101
#define EDIT_NEW 0
#define EDIT_ADD 1
#define EDIT_REMOVE 2
#define EDIT_LOAD 3
#define EDIT_SAVE 4
#define EDIT_SORT 5
CSong::CSong():_cRef(1)
{
}
void CSong::AddRef()
{
InterlockedIncrement((LONG*)&_cRef);
}
void CSong::Release()
{
InterlockedDecrement((LONG*)&_cRef);
if (_cRef == 0)
delete this;
}
CZaxxonEditor::CZaxxonEditor(CZaxxon* pz):_pzax(pz)
{
WNDCLASS wc = {0};
wc.lpszClassName = TEXT("ZaxxonEditor");
wc.lpfnWndProc = CZaxxonEditor::s_WndProc;
wc.hInstance = HINST_THISDLL;
wc.hbrBackground = HBRUSH(COLOR_ACTIVECAPTION + 1);
RegisterClass(&wc);
hSongList = DPA_Create(10);
}
int CALLBACK SongDestroyCallback(void* p, void*)
{
CSong* psong = (CSong*)p;
psong->Release();
return 1;
}
CZaxxonEditor::~CZaxxonEditor()
{
if (_hwnd)
{
DestroyWindow(_hwnd);
_hwnd = NULL;
}
DPA_DestroyCallback(hSongList, SongDestroyCallback, NULL);
}
int ReadLineFromStream(IStream* pstm, LPTSTR pszLine, DWORD cch)
{
// Assume this is an ANSI string
char szLine[MAX_PATH];
DWORD dwRead;
int iLine = 0;
ULARGE_INTEGER UliStartingFrom;
LARGE_INTEGER liSeek = {0};
pstm->Seek(liSeek, STREAM_SEEK_CUR, &UliStartingFrom);
pstm->Read(szLine, ARRAYSIZE(szLine), &dwRead);
if (dwRead > 0)
{
// Now convert to String
SHAnsiToUnicode(szLine, pszLine, dwRead);
pszLine[MAX_PATH - 1] = '\0'; //Null terminate the string
// Get the number of characters in the line
LPTSTR pszNewLine = StrStr(pszLine, TEXT("\r"));
if (pszNewLine)
{
*pszNewLine++ = TEXT('\0'); // Nuke \r
*pszNewLine = TEXT('\0'); // Nuke \n
}
else
{
pszNewLine = StrStr(pszLine, TEXT("\n"));
if (pszNewLine)
*pszNewLine = TEXT('\0'); // Nuke \n
}
iLine = lstrlen(pszLine);
// Now take that, and set the seek position to 2 more than that (One for the \r and \n
liSeek.QuadPart = UliStartingFrom.QuadPart + iLine + 1 + 1;
pstm->Seek(liSeek, STREAM_SEEK_SET, NULL);
}
return iLine;
}
void CZaxxonEditor::UpdateSong(CSong* psong)
{
int i = ListView_MapIDToIndex(_hwndList, psong->_id);
if (i >= 0)
{
TCHAR szTitle[MAX_PATH];
wsprintf(szTitle, TEXT("%s - %s"), psong->szArtist, psong->szTitle);
LVITEM lv;
lv.mask = LVIF_TEXT;
lv.iItem = i;
lv.iSubItem = 0;
lv.pszText = szTitle;
ListView_SetItem(_hwndList,&lv);
lv.iSubItem = 1;
lv.pszText = psong->szDuration;
ListView_SetItem(_hwndList,&lv);
}
}
void CZaxxonEditor::LoadPlaylist()
{
TCHAR sz[MAX_PATH];
TCHAR szInit[MAX_PATH];
OPENFILENAME of = {0};
of.lStructSize = sizeof(of);
of.hwndOwner = _hwnd;
of.lpstrFilter = TEXT("Playlist\0*.m3u\0\0");
of.lpstrFile = sz;
of.nMaxFile = MAX_PATH;
of.lpstrDefExt = TEXT(".m3u");
if (FAILED(SHGetFolderPath(NULL, CSIDL_MYMUSIC, NULL, SHGFP_TYPE_CURRENT, szInit)))
SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, szInit);
of.lpstrInitialDir = szInit;
of.Flags = OFN_ENABLESIZING | OFN_EXPLORER | OFN_FILEMUSTEXIST;
if (GetOpenFileName(&of))
{
ClearPlaylist();
IStream* pstm;
if (SUCCEEDED(SHCreateStreamOnFile(sz, STGM_READ, &pstm)))
{
do
{
TCHAR szFilename[MAX_PATH];
if (ReadLineFromStream(pstm, szFilename, ARRAYSIZE(szFilename)) > 0)
{
AddFilename(szFilename);
}
else
{
break;
}
}
while (1);
pstm->Release();
}
}
}
void CZaxxonEditor::SavePlaylist()
{
TCHAR sz[MAX_PATH];
TCHAR szInit[MAX_PATH];
OPENFILENAME of = {0};
of.lStructSize = sizeof(of);
of.hwndOwner = _hwnd;
of.lpstrFilter = TEXT("Playlist\0*.m3u\0\0");
of.lpstrFile = sz;
of.nMaxFile = MAX_PATH;
of.lpstrDefExt = TEXT(".m3u");
if (FAILED(SHGetFolderPath(NULL, CSIDL_MYMUSIC, NULL, SHGFP_TYPE_CURRENT, szInit)))
SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, szInit);
of.lpstrInitialDir = szInit;
of.Flags = OFN_ENABLESIZING | OFN_EXPLORER;
if (GetSaveFileName(&of))
{
IStream* pstm;
if (SUCCEEDED(SHCreateStreamOnFile(sz, STGM_CREATE | STGM_WRITE, &pstm)))
{
char szLine[MAX_PATH];
int cCount = DPA_GetPtrCount(hSongList);
for (int i=0; i < cCount; i++)
{
CSong* psong = (CSong*)DPA_FastGetPtr(hSongList, i);
SHUnicodeToAnsi(psong->szSong, szLine, MAX_PATH);
pstm->Write(szLine, lstrlenA(szLine), NULL);
pstm->Write("\r\n", 2, NULL);
}
pstm->Write("\r\n", 2, NULL);
pstm->Release();
}
}
}
void CZaxxonEditor::RemoveFromPlaylist()
{
int iItem = ListView_GetNextItem(_hwndList, -1, MAKELPARAM(LVNI_SELECTED, 0));
if (iItem >= 0 && iItem < DPA_GetPtrCount(hSongList))
{
_pzax->_pzax->RemoveSong(iItem);
ListView_DeleteItem(_hwndList, iItem);
CSong* psong = (CSong*)DPA_DeletePtr(hSongList, iItem);
psong->Release();
}
}
void CZaxxonEditor::ClearPlaylist()
{
_pzax->_pzax->ClearPlaylist();
DPA_DestroyCallback(hSongList, SongDestroyCallback, NULL);
hSongList = DPA_Create(10);
ListView_DeleteAllItems(_hwndList);
}
void CZaxxonEditor::InsertFilename(int i, PTSTR psz)
{
CSong* pzs = new CSong;
if (pzs)
{
StrCpy(pzs->szSong, psz);
int iIndex = DPA_InsertPtr(hSongList, i, pzs);
if (iIndex != -1)
{
LVITEM lv;
lv.mask = LVIF_TEXT;
lv.iItem = iIndex;
lv.iSubItem = 0;
lv.pszText = PathFindFileName(psz);
ListView_InsertItem(_hwndList, &lv);
pzs->_id = ListView_MapIndexToID(_hwndList, iIndex);
_pzax->_pzax->AddSong(psz);
CSongExtractionTask* pset = new CSongExtractionTask(_hwnd, pzs);
if (pset)
{
if (_pzax->_pScheduler)
_pzax->_pScheduler->AddTask(pset, CLSID_Zaxxon, 0, ITSAT_MIN_PRIORITY);
pset->Release();
}
}
}
}
void CZaxxonEditor::AddFilename(PTSTR psz)
{
InsertFilename(DSA_APPEND, psz);
}
void CZaxxonEditor::AddToPlaylist()
{
OPENFILENAME of = {0};
PTSTR psz = (PTSTR)LocalAlloc(LPTR, 4096);
if (psz)
{
of.lStructSize = sizeof(of);
of.hwndOwner = _hwnd;
of.lpstrFilter = TEXT("Music\0*.mp3;*.wma\0\0");
of.lpstrFile = psz;
of.nMaxFile = 4096;
of.Flags = OFN_ALLOWMULTISELECT | OFN_ENABLESIZING | OFN_EXPLORER | OFN_FILEMUSTEXIST;
if (GetOpenFileName(&of))
{
TCHAR szName[MAX_PATH];
PTSTR pszFilename = psz;
int iLen = lstrlen(pszFilename);
do
{
pszFilename += iLen + 1; // Skip the last filename
iLen = lstrlen(pszFilename);
if (iLen > 0)
{
StrCpy(szName, psz); // Copy the path;
PathAppend(szName, pszFilename); // Append the new name
AddFilename(szName); // Append to list.
}
}
while (iLen != 0);
}
LocalFree(psz);
}
}
int FindSong(void* p1, void* p2, LPARAM lParam)
{
PTSTR pszSong = (PTSTR)p1;
CSong* psong = (CSong*)p2;
return StrCmpI(pszSong, psong->szSong);
}
void CZaxxonEditor::HighlightSong(PTSTR psz)
{
int iItem = DPA_Search(hSongList, psz, 0, FindSong, 0, NULL);
if (iItem >= 0)
{
ListView_SetItemState(_hwndList, iItem, LVIS_SELECTED, LVIS_SELECTED);
}
}
static const TCHAR* g_EditStrings = TEXT("New\0Add\0Delete\0Load\0Save\0Sort\0\0");
static const TBBUTTON g_crgEditButtons[] =
{
{I_IMAGENONE, EDIT_NEW, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, 0, 0},
{I_IMAGENONE, EDIT_ADD, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, 0, 1},
{I_IMAGENONE, EDIT_REMOVE, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, 0, 2},
{I_IMAGENONE, EDIT_LOAD, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, 0, 3},
{I_IMAGENONE, EDIT_SAVE, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, 0, 4},
{I_IMAGENONE, EDIT_SORT, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, 0, 5},
};
BOOL CZaxxonEditor::Initialize()
{
int cxInitial = 300;
int cyInitial = 500;
_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
TEXT("ZaxxonEditor"), TEXT("Zaxxon Playlist Editor"),
WS_CAPTION | WS_POPUPWINDOW | WS_THICKFRAME | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, 0, 0,
_pzax->GetHWND(), NULL, HINST_THISDLL, this);
if (_hwnd)
{
_pzax->_pzax->Register(_hwnd);
_hwndList = CreateWindow(
TEXT("SysListView32"), TEXT(""),
WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS,
0, 0, 0, 0,
_hwnd, (HMENU)ID_LISTVIEW, NULL, NULL);
if (_hwndList)
{
RECT rc;
GetClientRect(_hwnd, &rc);
LVCOLUMN lvc = {0};
lvc.mask = LVCF_TEXT | LVCF_WIDTH;
lvc.cx = 4 * cxInitial / 5 - 10;
lvc.pszText = TEXT("Song");
ListView_InsertColumn(_hwndList, 0, &lvc);
lvc.mask = LVCF_TEXT | LVCF_WIDTH;
lvc.cx = cxInitial / 5;
lvc.pszText = TEXT("Length");
ListView_InsertColumn(_hwndList, 1, &lvc);
ListView_SetExtendedListViewStyle(_hwndList, LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
}
_hwndToolbar = CreateWindowEx(WS_EX_TOOLWINDOW, TOOLBARCLASSNAME, NULL,
WS_VISIBLE | WS_CHILD | TBSTYLE_FLAT | TBSTYLE_LIST |
WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
CCS_NODIVIDER | CCS_NOPARENTALIGN |
CCS_NORESIZE | TBSTYLE_REGISTERDROP,
0, 0, 0, 0, _hwnd, (HMENU) ID_TOOLBAR, HINST_THISDLL, NULL);
if (_hwndToolbar)
{
// Set the format to ANSI or UNICODE as appropriate.
ToolBar_SetUnicodeFormat(_hwndToolbar, TRUE);
SetWindowTheme(_hwndToolbar, L"TaskBar", NULL);
SendMessage(_hwndToolbar, CCM_SETVERSION, COMCTL32_VERSION, 0);
SendMessage(_hwndToolbar, TB_BUTTONSTRUCTSIZE, SIZEOF(TBBUTTON), 0);
SendMessage(_hwndToolbar, TB_ADDSTRING, NULL, (LPARAM)g_EditStrings);
SendMessage(_hwndToolbar, TB_ADDBUTTONS, ARRAYSIZE(g_crgEditButtons), (LPARAM)&g_crgEditButtons);
ToolBar_SetExtendedStyle(_hwndToolbar, TBSTYLE_EX_DOUBLEBUFFER, TBSTYLE_EX_DOUBLEBUFFER);
}
SetWindowPos(_hwnd, NULL, 0, 0, cxInitial, cyInitial, SWP_NOACTIVATE);
}
return _hwnd != NULL;
}
BOOL CZaxxonEditor::Show(BOOL fShow)
{
ShowWindow(_hwnd, fShow?SW_SHOW:SW_HIDE);
return TRUE;
}
LRESULT CZaxxonEditor::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
break;
case WM_SONGCHANGE:
{
_fIgnoreChange = TRUE;
HighlightSong((PTSTR)wParam);
_fIgnoreChange = FALSE;
}
break;
case WM_UPDATESONG:
{
UpdateSong((CSong*)wParam);
}
break;
case WM_NOTIFY:
{
LPNMHDR pnm = (LPNMHDR)lParam;
if (pnm->idFrom == ID_TOOLBAR)
{
if (pnm->code == NM_CLICK)
{
{
int idCmd = (int)((LPNMCLICK)pnm)->dwItemSpec;
switch (idCmd)
{
case EDIT_NEW:
ClearPlaylist();
break;
case EDIT_ADD:
AddToPlaylist();
break;
case EDIT_REMOVE:
RemoveFromPlaylist();
break;
case EDIT_LOAD:
LoadPlaylist();
break;
case EDIT_SAVE:
SavePlaylist();
break;
case EDIT_SORT:
break;
}
}
}
}
else if (pnm->idFrom == ID_LISTVIEW)
{
if (pnm->code == LVN_ITEMCHANGED && !_fIgnoreChange)
{
NMLISTVIEW* pnml = (NMLISTVIEW*)pnm;
int iItem = pnml->iItem;
if (iItem < DPA_GetPtrCount(hSongList) &&
pnml->uNewState != pnml->uOldState &&
pnml->uNewState & LVIS_SELECTED)
{
_pzax->_pzax->SetSong(iItem);
}
}
}
}
break;
case WM_CLOSE:
ShowWindow(hwnd, SW_HIDE);
return 1;
case WM_SIZE:
{
RECT rc;
GetClientRect(hwnd, &rc);
LONG lButton = SendMessage(_hwndToolbar, TB_GETBUTTONSIZE, 0, 0L);
SetWindowPos(_hwndList, NULL, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc) - HIWORD(lButton), SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(_hwndToolbar, NULL, rc.left, RECTHEIGHT(rc) - HIWORD(lButton), RECTWIDTH(rc), HIWORD(lButton), SWP_NOZORDER | SWP_NOACTIVATE);
}
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
LRESULT CZaxxonEditor::s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_CREATE)
{
SetWindowLongPtr(hwnd, GWLP_USERDATA, (ULONG_PTR)((CREATESTRUCT*)lParam)->lpCreateParams);
}
CZaxxonEditor* pedit = (CZaxxonEditor*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (pedit)
return pedit->WndProc(hwnd, uMsg, wParam, lParam);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
CSongExtractionTask::CSongExtractionTask(HWND hwnd, CSong* psong)
: CRunnableTask(RTF_DEFAULT), _hwnd(hwnd)
{
_psong = psong;
_psong->AddRef();
}
CSongExtractionTask::~CSongExtractionTask()
{
_psong->Release();
}
STDMETHODIMP CSongExtractionTask::RunInitRT()
{
LPITEMIDLIST pidl = ILCreateFromPath(_psong->szSong);
if (pidl)
{
LPCITEMIDLIST pidlChild;
IShellFolder2* psf;
HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder2, &psf), &pidlChild);
if (SUCCEEDED(hr))
{
VARIANT v;
hr = psf->GetDetailsEx(pidlChild, &SCID_MUSIC_Artist, &v);
if (SUCCEEDED(hr))
{
VariantToStr(&v, _psong->szArtist, ARRAYSIZE(_psong->szArtist));
}
hr = psf->GetDetailsEx(pidlChild, &SCID_MUSIC_Album, &v);
if (SUCCEEDED(hr))
{
VariantToStr(&v, _psong->szAlbum, ARRAYSIZE(_psong->szAlbum));
}
hr = psf->GetDetailsEx(pidlChild, &SCID_Title, &v);
if (SUCCEEDED(hr))
{
VariantToStr(&v, _psong->szTitle, ARRAYSIZE(_psong->szTitle));
}
hr = psf->GetDetailsEx(pidlChild, &SCID_AUDIO_Duration, &v);
if (SUCCEEDED(hr))
{
PROPVARIANT pv = *(PROPVARIANT*)&v;
FILETIME ft = {pv.uhVal.LowPart, pv.uhVal.HighPart};
SYSTEMTIME st;
FileTimeToSystemTime(&ft, &st);
GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER,
&st, NULL, _psong->szDuration, ARRAYSIZE(_psong->szDuration));
}
SendMessage(_hwnd, WM_UPDATESONG, (WPARAM)_psong, 0);
psf->Release();
}
ILFree(pidl);
}
return S_OK;
}