diff options
Diffstat (limited to 'Src/Plugins/Library/ml_downloads')
51 files changed, 5605 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_downloads/AtomParse.h b/Src/Plugins/Library/ml_downloads/AtomParse.h new file mode 100644 index 00000000..ab76a1f9 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/AtomParse.h @@ -0,0 +1,51 @@ +#ifndef NULLSOFT_ATOMPARSEH +#define NULLSOFT_ATOMPARSEH +#if 0 +#include "RFCDate.h" +#include "XMLNode.h" +#include "Feeds.h" +#include "../nu/AutoChar.h" +#include "ChannelSync.h" + +void ReadAtomItem(XMLNode *item, Channel &channel) +{ +} + +void ReadAtomChannel(XMLNode *node, Channel &newChannel) +{ + XMLNode *curNode = 0; + curNode = node->Get(L"title"); + if (curNode) + newChanneltitle = curNode->content; + + curNode = node->Get(L"subtitle"); + if (curNode) + newChannel.description = curNode->content; + + XMLNode::NodeList &links = node->GetList(L"link"); + XMLNode::NodeList::iterator linkItr; + for (linkItr=links.begin();linkItr!=links.end();linkItr++) + { + if ((*linkItr)->properties[L"rel"].empty() + || (*linkItr)->properties[L"rel"]== L"alternate") + newChannel.link = (*linkItr)->properties[L"href"]; + } + + XMLNode::NodeList &entries = node->GetList(L"entry"); + XMLNode::NodeList::iterator entryItr; + for (entryItr=entries.begin();entryItr!=entries.end();entryItr++) + { + } +} + +void ReadAtom(XMLNode *atom, ChannelSync *sync) +{ + sync->BeginChannelSync(); + Channel newChannel; + ReadAtomChannel(atom, newChannel); + sync->NewChannel(newChannel); + + sync->EndChannelSync(); +} +#endif +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/DESIGN.txt b/Src/Plugins/Library/ml_downloads/DESIGN.txt new file mode 100644 index 00000000..47148235 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/DESIGN.txt @@ -0,0 +1,12 @@ +
+
+
+
+major components:
+
+Cloud
+-----
+
+The cloud is the object which is responsible for doing period updates on RSS feeds.
+It automatically scans the feeds. You can manually refresh via the Refresh(string url) method
+
diff --git a/Src/Plugins/Library/ml_downloads/Defaults.cpp b/Src/Plugins/Library/ml_downloads/Defaults.cpp new file mode 100644 index 00000000..0478d92b --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/Defaults.cpp @@ -0,0 +1,43 @@ +#include "main.h" +#include "Defaults.h" +#include <shlobj.h> +wchar_t defaultDownloadPath[MAX_PATH] = {0}; +bool needToMakePodcastsView=true; + +static BOOL UtilGetSpecialFolderPath( HWND hwnd, wchar_t *path, int folder ) +{ + ITEMIDLIST *pidl; // Shell Item ID List ptr + IMalloc *imalloc; // Shell IMalloc interface ptr + BOOL result; // Return value + + if ( SHGetSpecialFolderLocation( hwnd, folder, &pidl ) != NOERROR ) + return FALSE; + + result = SHGetPathFromIDList( pidl, path ); + + if ( SHGetMalloc( &imalloc ) == NOERROR ) + { + imalloc->Free( pidl ); + imalloc->Release(); + } + + return result; +} + +static void BuildDefaultDownloadPath( HWND hwnd ) +{ + wchar_t defaultPath[ MAX_PATH ] = L""; + if ( !UtilGetSpecialFolderPath( hwnd, defaultPath, CSIDL_MYMUSIC ) ) + UtilGetSpecialFolderPath( hwnd, defaultPath, CSIDL_PERSONAL ); + + lstrcpyn( defaultDownloadPath, defaultPath, MAX_PATH ); +} + +/* +Requires an HWND because some of the shell functions require one. Theoretically it could be NULL +*/ +void BuildDefaults( HWND hwnd ) +{ + BuildDefaultDownloadPath( hwnd ); +} + diff --git a/Src/Plugins/Library/ml_downloads/Defaults.h b/Src/Plugins/Library/ml_downloads/Defaults.h new file mode 100644 index 00000000..52afbd60 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/Defaults.h @@ -0,0 +1,24 @@ +#ifndef NULLSOFT_DEFAULTSH +#define NULLSOFT_DEFAULTSH +#include <windows.h> + +extern wchar_t defaultDownloadPath[MAX_PATH]; + +#define DOWNLOADSSOURCEWIDTHDEFAULT 200 +#define DOWNLOADSTITLEWIDTHDEFAULT 200 +#define DOWNLOADSPROGRESSWIDTHDEFAULT 100 +#define DOWNLOADSDATEWIDTHDEFAULTS 100 +#define DOWNLOADSSIZEWIDTHDEFAULTS 100 +#define DOWNLOADSPATHWIDTHDEFAULTS 200 + +extern int downloadsSourceWidth, + downloadsTitleWidth, + downloadsProgressWidth, + downloadsPathWidth, + downloadsSizeWidth, + downloadsDateWidth; + +extern bool needToMakePodcastsView; + +void BuildDefaults(HWND); +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/DownloadManager.cpp b/Src/Plugins/Library/ml_downloads/DownloadManager.cpp new file mode 100644 index 00000000..61f734b2 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/DownloadManager.cpp @@ -0,0 +1 @@ +#include "main.h"
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/DownloadStatus.cpp b/Src/Plugins/Library/ml_downloads/DownloadStatus.cpp new file mode 100644 index 00000000..fb11a773 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/DownloadStatus.cpp @@ -0,0 +1,158 @@ +#include "main.h" +#include "api__ml_downloads.h" +#include "DownloadStatus.h" +#include "DownloadsDialog.h" + +#include <strsafe.h> + +DownloadStatus downloadStatus; + +using namespace Nullsoft::Utility; + +DownloadStatus::Status::Status() +{ + Init(); +} + +DownloadStatus::Status::Status( size_t _downloaded, size_t _maxSize, const wchar_t *_source, const wchar_t *_title, const wchar_t *_path ) +{ + Init(); + + downloaded = _downloaded; + maxSize = _maxSize; + source = _wcsdup( _source ); + title = _wcsdup( _title ); + path = _wcsdup( _path ); +} + +const DownloadStatus::Status &DownloadStatus::Status::operator =( const DownloadStatus::Status © ) +{ + Reset(); + Init(); + + downloaded = copy.downloaded; + maxSize = copy.maxSize; + source = _wcsdup( copy.source ); + title = _wcsdup( copy.title ); + path = _wcsdup( copy.path ); + killswitch = copy.killswitch; + + return *this; +} + +DownloadStatus::Status::~Status() +{ + Reset(); +} + +void DownloadStatus::Status::Init() +{ + downloaded = 0; + maxSize = 0; + killswitch = 0; + source = 0; + title = 0; + path = 0; +} + +void DownloadStatus::Status::Reset() +{ + if ( source ) + { + free( source ); + source = 0; + } + + if ( title ) + { + free( title ); + title = 0; + } + + if ( path ) + { + free( path ); + path = 0; + } +} + +void DownloadStatus::AddDownloadThread(DownloadToken token, const wchar_t *source, const wchar_t *title, const wchar_t *path) +{ + { + AutoLock lock(statusLock); + downloads[token] = Status(0,0,source,title,path); + DownloadsUpdated(downloads[token],token); + } + + static pluginMessage p = {ML_MSG_DOWNLOADS_VIEW_POSITION, 0, 0, 0}; + PostMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&p, ML_IPC_SEND_PLUGIN_MESSAGE); +} + +void DownloadStatus::DownloadThreadDone(DownloadToken token) +{ + { + AutoLock lock(statusLock); + downloads.erase(token); + } + + static pluginMessage p = {ML_MSG_DOWNLOADS_VIEW_POSITION, 0, 0, 0}; + PostMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&p, ML_IPC_SEND_PLUGIN_MESSAGE); +} + +bool DownloadStatus::UpdateStatus(DownloadToken token, size_t downloaded, size_t maxSize) +{ + AutoLock lock(statusLock); + downloads[token].downloaded = downloaded; + downloads[token].maxSize = maxSize; + + return !!downloads[token].killswitch; +} + +bool DownloadStatus::CurrentlyDownloading() +{ + AutoLock lock(statusLock); + return !downloads.empty(); +} + +void DownloadStatus::GetStatusString( wchar_t *status, size_t len ) +{ + AutoLock lock( statusLock ); + Downloads::iterator itr; + size_t bytesDownloaded = 0, bytesTotal = 0, numDownloads = 0; + bool unknownTotal = false; + for ( itr = downloads.begin(); itr != downloads.end(); itr++ ) + { + Status &dlstatus = itr->second; + if ( dlstatus.maxSize ) + { + numDownloads++; + bytesDownloaded += dlstatus.downloaded; + bytesTotal += dlstatus.maxSize; + } + else // don't have a max size + { + if ( dlstatus.downloaded ) // if we've downloaded some then we just don't know the total + { + unknownTotal = true; + numDownloads++; + bytesDownloaded += dlstatus.downloaded; + } + } + } + + if ( 0 == numDownloads ) + { + status[ 0 ] = L'\0'; + } + else + { + wchar_t buf[ 128 ] = { 0 }; + if ( unknownTotal || bytesTotal == 0 ) + StringCchPrintf( status, len, WASABI_API_LNGSTRINGW( IDS_DOWNLOADING_COMPLETE ), numDownloads, numDownloads, WASABI_API_LNG->FormattedSizeString( buf, ARRAYSIZE( buf ), bytesDownloaded ) ); + else + { + wchar_t buf2[ 128 ] = { 0 }; + StringCchPrintf( status, len, WASABI_API_LNGSTRINGW( IDS_DOWNLOADING_PROGRESS ), numDownloads, WASABI_API_LNG->FormattedSizeString( buf, ARRAYSIZE( buf ), bytesDownloaded ), WASABI_API_LNG->FormattedSizeString( buf2, ARRAYSIZE( buf2 ), bytesTotal ), MulDiv( (int)bytesDownloaded, 100, (int)bytesTotal ) ); + } + } +} diff --git a/Src/Plugins/Library/ml_downloads/DownloadStatus.h b/Src/Plugins/Library/ml_downloads/DownloadStatus.h new file mode 100644 index 00000000..8e8eedbb --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/DownloadStatus.h @@ -0,0 +1,41 @@ +#ifndef NULLSOFT_DOWNLOADSTATUSH +#define NULLSOFT_DOWNLOADSTATUSH + +#include "../nu/AutoLock.h" +#include <map> + + +class DownloadStatus +{ +public: + class Status + { + public: + Status(); + ~Status(); + const Status &operator =(const Status ©); + Status(size_t _downloaded, size_t _maxSize, const wchar_t *source, const wchar_t *title, const wchar_t *path); + size_t downloaded, maxSize; + + int killswitch; + wchar_t *source; + wchar_t *title; + wchar_t *path; + private: + + void Init(); + void Reset(); + }; + + void AddDownloadThread(DownloadToken token, const wchar_t *source, const wchar_t *title, const wchar_t *path); + void DownloadThreadDone(DownloadToken token); + bool UpdateStatus(DownloadToken token, size_t downloaded, size_t maxSize); + bool CurrentlyDownloading(); + void GetStatusString(wchar_t *status, size_t len); + typedef std::map<DownloadToken, Status> Downloads; + Downloads downloads; + Nullsoft::Utility::LockGuard statusLock; +}; + +extern DownloadStatus downloadStatus; +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/DownloadThread.cpp b/Src/Plugins/Library/ml_downloads/DownloadThread.cpp new file mode 100644 index 00000000..563c9d3d --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/DownloadThread.cpp @@ -0,0 +1,65 @@ +#include "Main.h" + +#pragma warning(disable:4786) + +#include "DownloadThread.h" +#include "api__ml_downloads.h" +#include <api/service/waServiceFactory.h> +#include "errors.h" +#include <strsafe.h> + +DownloadThread::DownloadThread() : parser( 0 ), parserFactory( 0 ) +{ + parserFactory = plugin.service->service_getServiceByGuid( obj_xmlGUID ); + if ( parserFactory ) + parser = (obj_xml *)parserFactory->getInterface(); + + if ( parser ) + { + parser->xmlreader_setCaseSensitive(); + parser->xmlreader_registerCallback( L"*", &xmlDOM ); + parser->xmlreader_open(); + } +} + +DownloadThread::~DownloadThread() +{ + if ( parser ) + { + parser->xmlreader_unregisterCallback( &xmlDOM ); + parser->xmlreader_close(); + } + + if ( parserFactory && parser ) + parserFactory->releaseInterface( parser ); + + parserFactory = 0; + parser = 0; +} + +#define FILE_BUFFER_SIZE 32768 +void DownloadThread::DownloadFile( const wchar_t *fileName ) +{ + if ( !parser ) + return; // no sense in continuing if there's no parser available + + HANDLE file = CreateFile( fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL ); + if ( file == INVALID_HANDLE_VALUE ) + return; + + while ( true ) + { + char data[ FILE_BUFFER_SIZE ] = { 0 }; + DWORD bytesRead = 0; + if ( ReadFile( file, data, FILE_BUFFER_SIZE, &bytesRead, NULL ) && bytesRead ) + { + parser->xmlreader_feed( (void *)data, bytesRead ); + } + else + break; + } + + CloseHandle( file ); + parser->xmlreader_feed( 0, 0 ); + ReadNodes( fileName ); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/DownloadThread.h b/Src/Plugins/Library/ml_downloads/DownloadThread.h new file mode 100644 index 00000000..31fad863 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/DownloadThread.h @@ -0,0 +1,27 @@ +#ifndef NULLSOFT_DOWNLOADTHREADH +#define NULLSOFT_DOWNLOADTHREADH + +#include "../xml/obj_xml.h" +#include "../xml/XMLDOM.h" +#include "../nu/Alias.h" +#include "api__ml_downloads.h" +#include <api/service/waServiceFactory.h> + + +class DownloadThread +{ +public: + DownloadThread(); + virtual ~DownloadThread(); + + virtual void ReadNodes(const wchar_t *url) = 0; + + void DownloadFile(const wchar_t *fileName); +protected: + XMLDOM xmlDOM; +private: + obj_xml *parser; + waServiceFactory *parserFactory; + +}; +#endif diff --git a/Src/Plugins/Library/ml_downloads/DownloadViewCallback.cpp b/Src/Plugins/Library/ml_downloads/DownloadViewCallback.cpp new file mode 100644 index 00000000..ffd2ca52 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/DownloadViewCallback.cpp @@ -0,0 +1,125 @@ +#include "Main.h" + +//#include "../Components/wac_downloads/wac_downloads_download_manager.h" + +using namespace Nullsoft::Utility; + +DownloadViewCallback::DownloadViewCallback() +{} + +void DownloadViewCallback::OnInit( DownloadToken token ) +{ + // ---- Inform the download status service of our presence---- + downloadStatus.AddDownloadThread( token, WAC_API_DOWNLOADMANAGER->GetSource( token ), WAC_API_DOWNLOADMANAGER->GetTitle( token ), WAC_API_DOWNLOADMANAGER->GetLocation( token ) ); +} + +void DownloadViewCallback::OnConnect( DownloadToken token ) +{} + +void DownloadViewCallback::OnData( DownloadToken token, void *data, size_t datalen ) +{ + if ( token == NULL ) + return; + + api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver( token ); + size_t totalSize = http->content_length(); + size_t downloaded = (size_t)WAC_API_DOWNLOADMANAGER->GetBytesDownloaded( token ); + + // if killswitch is turned on, then cancel download + downloadStatus.UpdateStatus( token, downloaded, totalSize ); + dirty++; +} + +void DownloadViewCallback::OnCancel( DownloadToken token ) +{ + api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver( token ); + + DownloadedFile *data = new DownloadedFile( AutoWide( http->get_url() ), WAC_API_DOWNLOADMANAGER->GetLocation( token ), WAC_API_DOWNLOADMANAGER->GetSource( token ), WAC_API_DOWNLOADMANAGER->GetTitle( token ), DownloadedFile::DOWNLOAD_CANCELED, _time64( 0 ) ); + + data->totalSize = http->content_length(); + data->bytesDownloaded = (size_t)WAC_API_DOWNLOADMANAGER->GetBytesDownloaded( token ); + + { + AutoLock lock( downloadedFiles ); + downloadedFiles.downloadList.push_back( *data ); + DownloadsUpdated( token, &downloadedFiles.downloadList[ downloadedFiles.downloadList.size() - 1 ] ); + } + + downloadStatus.DownloadThreadDone( token ); + delete data; + dirty++; +} + +void DownloadViewCallback::OnError( DownloadToken token, int error ) +{ + api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver( token ); + + DownloadedFile *data = new DownloadedFile( AutoWide( http->get_url() ), WAC_API_DOWNLOADMANAGER->GetLocation( token ), WAC_API_DOWNLOADMANAGER->GetSource( token ), WAC_API_DOWNLOADMANAGER->GetTitle( token ), DownloadedFile::DOWNLOAD_FAILURE, _time64( 0 ) ); + + data->totalSize = http->content_length(); + data->bytesDownloaded = (size_t)WAC_API_DOWNLOADMANAGER->GetBytesDownloaded( token ); + + { + AutoLock lock( downloadedFiles ); + downloadedFiles.downloadList.push_back( *data ); + DownloadsUpdated( token, &downloadedFiles.downloadList[ downloadedFiles.downloadList.size() - 1 ] ); + } + + downloadStatus.DownloadThreadDone( token ); + delete data; + dirty++; +} + +void DownloadViewCallback::OnFinish( DownloadToken token ) +{ + api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver( token ); + + DownloadedFile *data = new DownloadedFile( AutoWide( http->get_url() ), WAC_API_DOWNLOADMANAGER->GetLocation( token ), WAC_API_DOWNLOADMANAGER->GetSource( token ), WAC_API_DOWNLOADMANAGER->GetTitle( token ), DownloadedFile::DOWNLOAD_SUCCESS, _time64( 0 ) ); + + data->totalSize = http->content_length(); + data->bytesDownloaded = (size_t)WAC_API_DOWNLOADMANAGER->GetBytesDownloaded( token ); + + { + AutoLock lock( downloadedFiles ); + downloadedFiles.downloadList.push_back( *data ); + + //AddDownloadData(*data); + + DownloadsUpdated( token, &downloadedFiles.downloadList[ downloadedFiles.downloadList.size() - 1 ] ); + } + + downloadStatus.DownloadThreadDone( token ); + delete data; + dirty++; +} + + +size_t DownloadViewCallback::AddRef() +{ + return this->_ref_count.fetch_add( 1 ); +} + +size_t DownloadViewCallback::Release() +{ + std::size_t l_ref_countr = this->_ref_count.fetch_sub( 1 ); + if ( l_ref_countr == 0 ) + delete( this ); + + return l_ref_countr; +} + +DownloadViewCallback::~DownloadViewCallback() +{} + +#define CBCLASS DownloadViewCallback +START_DISPATCH; +VCB( IFC_DOWNLOADMANAGERCALLBACK_ONFINISH, OnFinish ) +VCB( IFC_DOWNLOADMANAGERCALLBACK_ONCANCEL, OnCancel ) +VCB( IFC_DOWNLOADMANAGERCALLBACK_ONERROR, OnError ) +VCB( IFC_DOWNLOADMANAGERCALLBACK_ONDATA, OnData ) +VCB( IFC_DOWNLOADMANAGERCALLBACK_ONCONNECT, OnConnect ) +VCB( IFC_DOWNLOADMANAGERCALLBACK_ONINIT, OnInit ) +CB( ADDREF, AddRef ) +CB( RELEASE, Release ) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/DownloadViewCallback.h b/Src/Plugins/Library/ml_downloads/DownloadViewCallback.h new file mode 100644 index 00000000..6ecce16b --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/DownloadViewCallback.h @@ -0,0 +1,37 @@ +#include <atomic> + +#include "Main.h" +#include "Downloaded.h" + +#include "DownloadStatus.h" +#include "DownloadsDialog.h" +#include "api__ml_downloads.h" +#include "api/service/waServiceFactory.h" +#include "../../..\Components\wac_network\wac_network_http_receiver_api.h" + + +class DownloadViewCallback : public ifc_downloadManagerCallback +{ +public: + DownloadViewCallback(); + + void OnInit( DownloadToken token ); + void OnConnect( DownloadToken token ); + void OnData( DownloadToken token, void *data, size_t datalen ); + void OnCancel( DownloadToken token ); + void OnError( DownloadToken token, int error ); + void OnFinish( DownloadToken token ); + + size_t AddRef(); + size_t Release(); + +private: // private destructor so no one accidentally calls delete directly on this reference counted object + ~DownloadViewCallback(); + +protected: + RECVS_DISPATCH; + +private: + std::atomic<std::size_t> _ref_count = 1; +}; + diff --git a/Src/Plugins/Library/ml_downloads/Downloaded.cpp b/Src/Plugins/Library/ml_downloads/Downloaded.cpp new file mode 100644 index 00000000..dc5a0e19 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/Downloaded.cpp @@ -0,0 +1,141 @@ +#include "main.h" +#include "Downloaded.h" + +DownloadList downloadedFiles; +using namespace Nullsoft::Utility; +Nullsoft::Utility::LockGuard downloadedLock; + +DownloadedFile::DownloadedFile() +{ + Init(); +} + +DownloadedFile::DownloadedFile( const wchar_t *_url, const wchar_t *_path, const wchar_t *_source, const wchar_t *_title, int downloadStatus, __time64_t downloadDate ) +{ + Init(); + + this->downloadStatus = downloadStatus; + this->downloadDate = downloadDate; + + SetSource( _source ); + SetTitle( _title ); + SetPath( _path ); + SetURL( _url ); +} + +DownloadedFile::DownloadedFile( const DownloadedFile © ) +{ + Init(); + + operator =( copy ); +} + +DownloadedFile::~DownloadedFile() +{ + Reset(); +} + + +void DownloadedFile::Init() +{ + url = 0; + path = 0; + source = 0; + title = 0; + bytesDownloaded = 0; + totalSize = 0; + downloadDate = 0; +} + +void DownloadedFile::Reset() +{ + if ( url ) + { + free( url ); + url = 0; + } + + if ( path ) + { + free( path ); + path = 0; + } + + if ( source ) + { + free( source ); + source = 0; + } + + if ( title ) + { + free( title ); + title = 0; + } +} + +void DownloadedFile::SetPath( const wchar_t *_path ) +{ + if ( path ) + free( path ); + + path = _wcsdup( _path ); +} + +void DownloadedFile::SetURL( const wchar_t *_url ) +{ + if ( url ) + free( url ); + + url = _wcsdup( _url ); +} + +void DownloadedFile::SetTitle( const wchar_t *_title ) +{ + if ( title ) + free( title ); + + title = _wcsdup( _title ); +} + +void DownloadedFile::SetSource( const wchar_t *_source ) +{ + if ( source ) + free( source ); + + source = _wcsdup( _source ); +} + +const DownloadedFile &DownloadedFile::operator =( const DownloadedFile © ) +{ + Reset(); + Init(); + + SetSource( copy.source ); + SetTitle( copy.title ); + + bytesDownloaded = copy.bytesDownloaded; + totalSize = copy.totalSize; + downloadStatus = copy.downloadStatus; + downloadDate = copy.downloadDate; + + SetPath( copy.path ); + SetURL( copy.url ); + + return *this; +} + +wchar_t *GetDownloadStatus( int downloadStatus ) +{ + switch ( downloadStatus ) + { + case DownloadedFile::DOWNLOAD_SUCCESS: + return WASABI_API_LNGSTRINGW( IDS_DOWNLOAD_SUCCESS ); + case DownloadedFile::DOWNLOAD_FAILURE: + return WASABI_API_LNGSTRINGW( IDS_DOWNLOAD_FAILURE ); + case DownloadedFile::DOWNLOAD_CANCELED: + return WASABI_API_LNGSTRINGW( IDS_DOWNLOAD_CANCELED ); + default: + return WASABI_API_LNGSTRINGW( IDS_DOWNLOAD_FAILURE ); + } +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/Downloaded.h b/Src/Plugins/Library/ml_downloads/Downloaded.h new file mode 100644 index 00000000..17897f19 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/Downloaded.h @@ -0,0 +1,86 @@ +#ifndef NULLSOFT_DOWNLOADEDH +#define NULLSOFT_DOWNLOADEDH + +#include "../nu/AutoLock.h" +#include "../nu/AutoCharFn.h" +#include "..\..\General\gen_ml/ml.h" +#include <vector> +#include "../nu/MediaLibraryInterface.h" + +class DownloadedFile +{ +public: + DownloadedFile(); + DownloadedFile( const wchar_t *_url, const wchar_t *_path, const wchar_t *_source, const wchar_t *_title, int downloadStatus, __time64_t downloadDate ); + DownloadedFile( const DownloadedFile © ); + ~DownloadedFile(); + + const DownloadedFile &operator =( const DownloadedFile © ); + + void SetPath( const wchar_t *_path ); + void SetURL( const wchar_t *_url ); + void SetTitle( const wchar_t *_title ); + void SetSource( const wchar_t *_source ); + + enum + { + DOWNLOAD_FAILURE = 0, + DOWNLOAD_SUCCESS = 1, + DOWNLOAD_CANCELED = 2, + }; + + size_t bytesDownloaded = 0; + size_t totalSize = 0; + + int downloadStatus = 0; + __time64_t downloadDate = 0; + + wchar_t *path = 0; + wchar_t *url = 0; + wchar_t *source = 0; + wchar_t *title = 0; + + +private: + void Init(); + void Reset(); +}; + +class DownloadList +{ +public: + typedef std::vector<DownloadedFile> DownloadedFileList; + typedef DownloadedFileList::iterator iterator; + typedef DownloadedFileList::const_iterator const_iterator; + + operator Nullsoft::Utility::LockGuard &() { return downloadedLock; } + + void Remove( size_t index ) { downloadList.erase( downloadList.begin() + index ); } + + bool RemoveAndDelete( int index ) + { + SendMessage( mediaLibrary.library, WM_ML_IPC, (WPARAM)downloadList[ index ].path, ML_IPC_DB_REMOVEITEMW ); + + if ( !DeleteFile( downloadList[ index ].path ) && GetLastError() != ERROR_FILE_NOT_FOUND ) + return false; + + downloadList.erase( downloadList.begin() + index ); + + return true; + } + + DownloadedFileList downloadList; + Nullsoft::Utility::LockGuard downloadedLock; + + iterator begin() { return downloadList.begin(); } + iterator end() { return downloadList.end(); } +}; + +extern DownloadList downloadedFiles; +extern int downloadsItemSort; +extern bool downloadsSortAscending; + +void CleanupDownloads(); +wchar_t *GetDownloadStatus( int downloadStatus ); + +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/DownloadsDialog.cpp b/Src/Plugins/Library/ml_downloads/DownloadsDialog.cpp new file mode 100644 index 00000000..adb2b008 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/DownloadsDialog.cpp @@ -0,0 +1,1657 @@ +#include "main.h" +#include "api__ml_downloads.h" +#include "RFCDate.h" +#include "Downloaded.h" +#include "DownloadStatus.h" +#include "Defaults.h" +#include "../nu/listview.h" +#include "..\..\General\gen_ml/ml_ipc.h" +#include "..\..\General\gen_ml/menu.h" +#include <vector> +#include "../nu/menushortcuts.h" +#include <commctrl.h> +#include <shlwapi.h> +#include <shellapi.h> +#include <strsafe.h> +#include <algorithm> + +HWND downloads_window = 0; +extern int downloads_treeItem; +extern int no_auto_hide; +int groupBtn = 1, enqueuedef = 0, customAllowed = 0; +HMENU g_context_menus2 = NULL; +static viewButtons view; + +#ifndef HDF_SORTUP +#define HDF_SORTUP 0x0400 +#define HDF_SORTDOWN 0x0200 +#endif // !HDF_SORTUP + +using namespace Nullsoft::Utility; + +enum +{ + COL_TITLE = 0, + COL_PROGRESS, + COL_DATE, + COL_SOURCE, + COL_SIZE, + COL_PATH, + NUM_COLUMNS, +}; + +int downloadsSourceWidth = DOWNLOADSSOURCEWIDTHDEFAULT; +int downloadsTitleWidth = DOWNLOADSTITLEWIDTHDEFAULT; +int downloadsProgressWidth = DOWNLOADSPROGRESSWIDTHDEFAULT; +int downloadsDateWidth = DOWNLOADSDATEWIDTHDEFAULTS; +int downloadsSizeWidth = DOWNLOADSSIZEWIDTHDEFAULTS; +int downloadsPathWidth = DOWNLOADSPATHWIDTHDEFAULTS; + + +W_ListView downloadList; +int downloadsItemSort = 2; // -1 means no sort active +bool downloadsSortAscending = false; + +enum +{ + DOWNLOADSDIALOG_TIMER_UPDATESTATUSBAR = 0, +}; + +class DownloadListItem +{ +public: + DownloadedFile *f = NULL; + DownloadToken token = NULL; + wchar_t *source = 0; + wchar_t *title = 0; + wchar_t *path = 0; + wchar_t status[ 20 ] = { 0 }; + + DownloadListItem( DownloadedFile *fi ) + { + f = new DownloadedFile( *fi ); + ZeroMemory( status, sizeof( status ) ); + } + + DownloadListItem( DownloadToken p_token, const wchar_t *p_source, const wchar_t *p_title, const wchar_t *p_path, size_t p_downloaded, size_t p_maxSize ) : token( p_token ) + { + if ( p_maxSize ) + StringCchPrintf( status, 20, WASABI_API_LNGSTRINGW( IDS_DOWNLOADING_PERCENT ), (int)( p_downloaded / ( p_maxSize / 100 ) ) ); + else + { + if ( WAC_API_DOWNLOADMANAGER->IsPending( p_token ) ) + WASABI_API_LNGSTRINGW_BUF( IDS_DOWNLOAD_PENDING, status, 20 ); + else + WASABI_API_LNGSTRINGW_BUF( IDS_DOWNLOADING, status, 20 ); + } + + source = p_source ? _wcsdup( p_source ) : NULL; + title = p_title ? _wcsdup( p_title ) : NULL; + path = p_path ? _wcsdup( p_path ) : NULL; + } + + ~DownloadListItem() + { + clean(); + + if ( f ) + delete f; + } + + void clean() + { + if ( source ) + { + free( source ); + source = 0; + } + + if ( title ) + { + free( title ); + title = 0; + } + + if ( path ) + { + free( path ); + path = 0; + } + } +}; + +static std::vector<DownloadListItem*> listContents; + +bool GetDownload( int &download ) +{ + download = ListView_GetNextItem( downloadList.getwnd(), download, LVNI_ALL | LVNI_SELECTED ); + if ( download == -1 ) + return false; + else + return true; +} + +void Downloads_Play( bool enqueue = false ) +{ + int download = -1; + AutoLock lock( downloadedFiles ); + while ( GetDownload( download ) ) + { + if ( !enqueue ) + { + if ( listContents[ download ]->f ) + mediaLibrary.PlayFile( listContents[ download ]->f->path ); + else if ( listContents[ download ]->path ) + mediaLibrary.PlayFile( listContents[ download ]->path ); + + enqueue = true; + } + else + { + if ( listContents[ download ]->f ) + mediaLibrary.EnqueueFile( listContents[ download ]->f->path ); + else if ( listContents[ download ]->path ) + mediaLibrary.EnqueueFile( listContents[ download ]->path ); + } + } +} + +void DownloadsUpdated( const DownloadStatus::Status &s, DownloadToken token ) +{ + listContents.push_back( new DownloadListItem( token, s.source, s.title, s.path, s.downloaded, s.maxSize ) ); + + downloadList.SetVirtualCountAsync( (int)listContents.size() ); +} + +void DownloadsUpdated( DownloadToken token, const DownloadedFile *f ) +{ + for ( DownloadListItem *l_content : listContents ) + { + if ( l_content->token == token ) + { + l_content->token = 0; + if ( f ) + { + l_content->f = new DownloadedFile( *f ); + + l_content->clean(); + } + else + lstrcpyn( l_content->status, L"Error", 20 ); + + break; + } + } + + PostMessage( downloadList.getwnd(), LVM_REDRAWITEMS, 0, listContents.size() ); +} + +void DownloadsUpdated() +{ + for ( DownloadListItem *l_content : listContents ) + delete l_content; + + listContents.clear(); + + for ( DownloadedFile &l_download : downloadedFiles.downloadList ) + listContents.push_back( new DownloadListItem( &l_download ) ); + + { + AutoLock lock( downloadStatus.statusLock ); + for ( DownloadStatus::Downloads::iterator itr = downloadStatus.downloads.begin(); itr != downloadStatus.downloads.end(); itr++ ) + { + listContents.push_back( new DownloadListItem( itr->first, itr->second.source, itr->second.title, itr->second.path, itr->second.downloaded, itr->second.maxSize ) ); + } + } + + downloadList.SetVirtualCountAsync( (int)listContents.size() ); +// Navigation_ShowService( SERVICE_DOWNLOADS, SHOWMODE_AUTO ); +} + +static void CleanupDownloads() +{ + { + AutoLock lock( downloadedFiles ); + DownloadList::DownloadedFileList &downloads = downloadedFiles.downloadList; + DownloadList::iterator itr, next; + for ( itr = downloads.begin(); itr != downloads.end();) + { + next = itr; + ++next; + if ( !PathFileExists( itr->path ) ) + downloads.erase( itr ); + else + itr = next; + } + } + +// Navigation_ShowService(SERVICE_DOWNLOADS, SHOWMODE_AUTO); +} + +void Downloads_UpdateStatusBar(HWND hwndDlg) +{ + wchar_t status[256]=L""; + downloadStatus.GetStatusString(status, 256); + SetWindowText(GetDlgItem(hwndDlg, IDC_STATUS), status); +} + +void Downloads_Paint(HWND hwndDlg) +{ + int tab[] = { IDC_DOWNLOADLIST | DCW_SUNKENBORDER, }; + dialogSkinner.Draw(hwndDlg, tab, sizeof(tab) / sizeof(tab[0])); +} + + +static HRGN g_rgnUpdate = NULL; +static int offsetX = 0, 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_REMOVE, IDC_CLEANUP, IDC_STATUS, + GROUP_MAIN, IDC_DOWNLOADLIST + }; + + 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}; + WASABI_API_LNGSTRINGW_BUF(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_REMOVE: + case IDC_CLEANUP: + 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 }; + GetWindowTextW( pl->hwnd, buffer, ARRAYSIZE( buffer ) ); + + LRESULT idealSize = MLSkinnedButton_GetIdealSize( pl->hwnd, buffer ); + LONG width = LOWORD( idealSize ) + WASABI_API_APP->getScaleX( 6 ); + + 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_STATUS: + SETLAYOUTPOS(pl, rg.left, rg.top, rg.right - rg.left, (rg.bottom - rg.top)); + pl->flags |= (pl->cx > 16) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW; + break; + case IDC_DOWNLOADLIST: + 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->getScaleY( 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 ); + + 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); +} + +void Downloads_DisplayChange(HWND hwndDlg) +{ + ListView_SetTextColor(downloadList.getwnd(), dialogSkinner.Color(WADLG_ITEMFG)); + ListView_SetBkColor(downloadList.getwnd(), dialogSkinner.Color(WADLG_ITEMBG)); + ListView_SetTextBkColor(downloadList.getwnd(), dialogSkinner.Color(WADLG_ITEMBG)); + downloadList.SetFont(dialogSkinner.GetFont()); + LayoutWindows(hwndDlg, TRUE); +} + +static void DownloadsDialog_SkinControls(HWND hwnd, const INT *itemList, INT itemCount, UINT skinType, UINT skinStyle) +{ + MLSKINWINDOW skinWindow = {0}; + skinWindow.style = skinStyle; + skinWindow.skinType = skinType; + + for(INT i = 0; i < itemCount; i++) + { + skinWindow.hwndToSkin = GetDlgItem(hwnd, itemList[i]); + if (NULL != skinWindow.hwndToSkin) + { + MLSkinWindow(plugin.hwndLibraryParent, &skinWindow); + } + } +} + +static void DownloadDialog_InitializeList(HWND hwnd) +{ + HWND hControl = GetDlgItem(hwnd, IDC_DOWNLOADLIST); + if (NULL == hControl) return; + + UINT styleEx = (UINT)GetWindowLongPtr(hControl, GWL_EXSTYLE); + SetWindowLongPtr(hControl, GWL_EXSTYLE, styleEx & ~WS_EX_NOPARENTNOTIFY); + + styleEx = LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP; + SendMessage(hControl, LVM_SETEXTENDEDLISTVIEWSTYLE, styleEx, styleEx); + SendMessage(hControl, LVM_SETUNICODEFORMAT, (WPARAM)TRUE, 0L); + + MLSKINWINDOW skinWindow; + skinWindow.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWLVS_ALTERNATEITEMS; + skinWindow.skinType = SKINNEDWND_TYPE_LISTVIEW; + skinWindow.hwndToSkin = hControl; + MLSkinWindow(plugin.hwndLibraryParent, &skinWindow); +} + +bool COL_SOURCE_Sort(const DownloadListItem* item1, const DownloadListItem* item2) +{ + if (item1->f && item2->f) + return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, (item1->f->source), -1, (item2->f->source), -1)); + else if (!item1->f && !item2->f) + return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, (item1->source), -1, (item2->source), -1)); + else if (!item1->f) + return (FALSE == downloadsSortAscending)?0:1; + else //if (!item2->f) + return (FALSE == downloadsSortAscending)?1:0; + + //return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, + // (item1->f?item1->f->source:item1->source), -1, + // (item2->f?item2->f->source:item2->source), -1)); +} + +bool COL_TITLE_Sort(const DownloadListItem* item1, const DownloadListItem* item2) +{ + if (item1->f && item2->f) + return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, (item1->f->title), -1, (item2->f->title), -1)); + else if (!item1->f && !item2->f) + return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, (item1->title), -1, (item2->title), -1)); + else if (!item1->f) + return (FALSE == downloadsSortAscending)?0:1; + else //if (!item2->f) + return (FALSE == downloadsSortAscending)?1:0; + + //return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, + // (item1->f?item1->f->title:item1->title), -1, + // (item2->f?item2->f->title:item2->title), -1)); +} + +bool COL_PROGRESS_Sort( const DownloadListItem *item1, const DownloadListItem *item2 ) +{ + if ( item1->f && item2->f ) + return ( item1->f->downloadStatus > item2->f->downloadStatus ); + else if ( !item1->f && !item2->f ) + return ( item1->token < item2->token ); + else if ( !item1->f ) + return ( FALSE == downloadsSortAscending ) ? 0 : 1; + else //if (!item2->f) + return ( FALSE == downloadsSortAscending ) ? 1 : 0; + + //return ((item1->f?item1->f->downloadStatus:-1) < (item2->f?item2->f->downloadStatus:-1)); + + //return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, + // (item1->f?GetDownloadStatus(item1->f->downloadStatus):item1->status), -1, + // (item2->f?GetDownloadStatus(item2->f->downloadStatus):item2->status), -1)); +} + +bool COL_PATH_Sort(const DownloadListItem* item1, const DownloadListItem* item2) +{ + if (item1->f && item2->f) + return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, (item1->f->path), -1, (item2->f->path), -1)); + else if (!item1->f && !item2->f) + return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, (item1->path), -1, (item2->path), -1)); + else if (!item1->f) + return (FALSE == downloadsSortAscending)?0:1; + else //if (!item2->f) + return (FALSE == downloadsSortAscending)?1:0; + + //return (CSTR_LESS_THAN == CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, + // (item1->f?item1->f->path:item1->path), -1, + // (item2->f?item2->f->path:item2->path), -1)); +} + +bool COL_DATE_Sort(const DownloadListItem* item1, const DownloadListItem* item2) +{ + if (item1->f && item2->f) + return item1->f->downloadDate < item2->f->downloadDate; + else if (!item1->f && !item2->f) + return item1->token < item2->token; + else if (!item1->f) + return (FALSE == downloadsSortAscending)?0:1; + else //if (!item2->f) + return (FALSE == downloadsSortAscending)?1:0; +} + +bool COL_SIZE_Sort(const DownloadListItem* item1, const DownloadListItem* item2) +{ + if (item1->f && item2->f) + return item1->f->totalSize < item2->f->totalSize; + else if (!item1->f && !item2->f) + return item1->token < item2->token; + else if (!item1->f) + return (FALSE == downloadsSortAscending)?0:1; + else //if (!item2->f) + return (FALSE == downloadsSortAscending)?1:0; +} + +static BOOL Downloads_SortItems(int sortColumn) +{ + AutoLock lock (downloadedFiles); + switch (sortColumn) + { + case COL_TITLE: + std::sort(listContents.begin(), listContents.end(), COL_TITLE_Sort); + if (FALSE == downloadsSortAscending) std::reverse(listContents.begin(), listContents.end()); + return TRUE; + case COL_PROGRESS: + std::sort(listContents.begin(), listContents.end(), COL_PROGRESS_Sort); + if (FALSE == downloadsSortAscending) std::reverse(listContents.begin(), listContents.end()); + return TRUE; + case COL_DATE: + std::sort(listContents.begin(), listContents.end(), COL_DATE_Sort); + if (FALSE == downloadsSortAscending) std::reverse(listContents.begin(), listContents.end()); + return TRUE; + case COL_SOURCE: + std::sort(listContents.begin(), listContents.end(), COL_SOURCE_Sort); + if (FALSE == downloadsSortAscending) std::reverse(listContents.begin(), listContents.end()); + return TRUE; + case COL_SIZE: + std::sort(listContents.begin(), listContents.end(), COL_SIZE_Sort); + if (FALSE == downloadsSortAscending) std::reverse(listContents.begin(), listContents.end()); + return TRUE; + case COL_PATH: + std::sort(listContents.begin(), listContents.end(), COL_PATH_Sort); + if (FALSE == downloadsSortAscending) std::reverse(listContents.begin(), listContents.end()); + return TRUE; + } + return FALSE; +} + +static void Downloads_SetListSortColumn(HWND hwnd, INT listId, INT index, BOOL fAscending) +{ + HWND hItems = GetDlgItem(hwnd, listId); + if (NULL == hItems) return; + + HWND hHeader = (HWND)SNDMSG(hItems, LVM_GETHEADER, 0, 0L); + if (NULL == hHeader) return; + + HDITEM item; + item.mask = HDI_FORMAT; + // reset first (ml req) + INT count = (INT)SNDMSG(hHeader, HDM_GETITEMCOUNT, 0, 0L); + for (INT i = 0; i < count; i++) + { + if (index != i && FALSE != (BOOL)SNDMSG(hHeader, HDM_GETITEM, i, (LPARAM)&item)) + { + if (0 != ((HDF_SORTUP | HDF_SORTDOWN) & item.fmt)) + { + item.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN); + SNDMSG(hHeader, HDM_SETITEM, i, (LPARAM)&item); + } + } + } + + if (FALSE != (BOOL)SNDMSG(hHeader, HDM_GETITEM, index, (LPARAM)&item)) + { + INT fmt = item.fmt & ~(HDF_SORTUP | HDF_SORTDOWN); + fmt |= (FALSE == fAscending) ? HDF_SORTDOWN : HDF_SORTUP; + if (fmt != item.fmt) + { + item.fmt = fmt; + SNDMSG(hHeader, HDM_SETITEM, index, (LPARAM)&item); + } + } +} + +static BOOL Downloads_Sort(HWND hwnd, INT iColumn, bool fAscending) +{ + BOOL result = TRUE; + downloadsSortAscending = fAscending; + Downloads_SortItems(iColumn); + Downloads_SetListSortColumn(hwnd, IDC_DOWNLOADLIST, iColumn, fAscending); + + if (FALSE != result) + { + HWND hItems = GetDlgItem(hwnd, IDC_DOWNLOADLIST); + if (NULL != hItems) + InvalidateRect(hItems, NULL, TRUE); + } + + return TRUE; +} + +void Downloads_UpdateButtonText(HWND hwndDlg, int _enqueuedef) +{ + if (groupBtn) + { + switch(_enqueuedef) + { + case 1: + SetDlgItemTextW(hwndDlg, IDC_PLAY, view.enqueue); + customAllowed = FALSE; + break; + + default: + // 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 p = {ML_MSG_VIEW_BUTTON_HOOK_IN_USE, (INT_PTR)_enqueuedef, 0, 0}; + + wchar_t *pszTextW = (wchar_t *)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p); + 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) + SetDlgItemTextW(hwndDlg, IDC_PLAY, pszTextW); + customAllowed = TRUE; + } + else + { + SetDlgItemTextW(hwndDlg, IDC_PLAY, view.play); + customAllowed = FALSE; + } + break; + } + } +} + +static void Downloads_ManageButtons( HWND hwndDlg ) +{ + int has_selection = downloadList.GetSelectedCount(); + + const int buttonids[] = { IDC_PLAY, IDC_ENQUEUE, IDC_CUSTOM, IDC_REMOVE }; + for ( size_t i = 0; i != sizeof( buttonids ) / sizeof( buttonids[ 0 ] ); i++ ) + { + HWND controlHWND = GetDlgItem( hwndDlg, buttonids[ i ] ); + EnableWindow( controlHWND, has_selection ); + } +} + +void Downloads_Init(HWND hwndDlg) +{ + HWND hLibrary = plugin.hwndLibraryParent; + downloads_window = hwndDlg; + + if (!view.play) + { + SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_GET_VIEW_BUTTON_TEXT, (WPARAM)&view); + } + + HACCEL accel = WASABI_API_LOADACCELERATORSW(IDR_VIEW_DOWNLOAD_ACCELERATORS); + if (accel) + WASABI_API_APP->app_addAccelerators(hwndDlg, &accel, 1, TRANSLATE_MODE_CHILD); + + g_context_menus2 = WASABI_API_LOADMENU(IDR_MENU1); + groupBtn = ML_GROUPBTN_VAL(); + enqueuedef = (ML_ENQDEF_VAL() == 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 p = {ML_MSG_VIEW_BUTTON_HOOK, (INT_PTR)hwndDlg, (INT_PTR)MAKELONG(IDC_CUSTOM, IDC_ENQUEUE), (INT_PTR)L"ml_downloads"}; + wchar_t *pszTextW = (wchar_t *)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p); + 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; + + MLSkinWindow2(hLibrary, hwndDlg, SKINNEDWND_TYPE_AUTO, SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS); + + const INT szControls[] = {IDC_PLAY, IDC_ENQUEUE, IDC_CUSTOM}; + DownloadsDialog_SkinControls(hwndDlg, szControls, ARRAYSIZE(szControls), SKINNEDWND_TYPE_AUTO, + SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | (groupBtn ? SWBS_SPLITBUTTON : 0)); + + const INT szControlz[] = {IDC_REMOVE, IDC_CLEANUP, IDC_STATUS}; + DownloadsDialog_SkinControls(hwndDlg, szControlz, ARRAYSIZE(szControlz), SKINNEDWND_TYPE_AUTO, + SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS); + + DownloadDialog_InitializeList(hwndDlg); + Downloads_UpdateStatusBar(hwndDlg); + + downloadList.setwnd(GetDlgItem(hwndDlg, IDC_DOWNLOADLIST)); + downloadList.AddCol(WASABI_API_LNGSTRINGW(IDS_TITLE), downloadsTitleWidth); + downloadList.AddCol(WASABI_API_LNGSTRINGW(IDS_PROGRESS), downloadsProgressWidth); + downloadList.AddCol(WASABI_API_LNGSTRINGW(IDS_DATE), downloadsDateWidth); + downloadList.AddCol(WASABI_API_LNGSTRINGW(IDS_SOURCE), downloadsSourceWidth); + downloadList.AddCol(WASABI_API_LNGSTRINGW(IDS_SIZE), downloadsSizeWidth); + downloadList.AddCol(WASABI_API_LNGSTRINGW(IDS_PATH), downloadsPathWidth); + + DownloadsUpdated(); + + downloadList.SetVirtualCount((int)listContents.size()); + Downloads_UpdateButtonText(hwndDlg, enqueuedef == 1); + Downloads_ManageButtons(hwndDlg); + Downloads_DisplayChange(hwndDlg); + Downloads_Sort(hwndDlg, downloadsItemSort, downloadsSortAscending); + SetTimer(hwndDlg, DOWNLOADSDIALOG_TIMER_UPDATESTATUSBAR , 1000, 0); +} + +void Downloads_Timer( HWND hwndDlg, UINT timerId ) +{ + switch ( timerId ) + { + case DOWNLOADSDIALOG_TIMER_UPDATESTATUSBAR: + Downloads_UpdateStatusBar( hwndDlg ); + { + AutoLock lock( downloadStatus.statusLock ); + for ( DownloadListItem *l_content : listContents ) + { + if ( l_content->token ) + { + size_t d = downloadStatus.downloads[ l_content->token ].downloaded; + size_t s = downloadStatus.downloads[ l_content->token ].maxSize; + + if ( s ) + StringCchPrintf( l_content->status, 20, WASABI_API_LNGSTRINGW( IDS_DOWNLOADING_PERCENT ), (int)( d / ( s / 100 ) ) ); + else + { + if ( WAC_API_DOWNLOADMANAGER->IsPending( l_content->token ) ) + WASABI_API_LNGSTRINGW_BUF( IDS_DOWNLOAD_PENDING, l_content->status, 20 ); + else + WASABI_API_LNGSTRINGW_BUF( IDS_DOWNLOADING, l_content->status, 20 ); + } + + PostMessage( downloadList.getwnd(), LVM_REDRAWITEMS, 0, listContents.size() ); + } + } + } + break; + } +} + +static INT Downloads_GetListSortColumn(HWND hwnd, INT listId, bool *fAscending) +{ + HWND hItems = GetDlgItem(hwnd, listId); + if (NULL != hItems) + { + HWND hHeader = (HWND)SNDMSG(hItems, LVM_GETHEADER, 0, 0L); + if (NULL != hHeader) + { + HDITEM item; + item.mask = HDI_FORMAT; + + INT count = (INT)SNDMSG(hHeader, HDM_GETITEMCOUNT, 0, 0L); + for (INT i = 0; i < count; i++) + { + if (FALSE != (BOOL)SNDMSG(hHeader, HDM_GETITEM, i, (LPARAM)&item) && + 0 != ((HDF_SORTUP | HDF_SORTDOWN) & item.fmt)) + { + if (NULL != fAscending) + { + *fAscending = (0 != (HDF_SORTUP & item.fmt)); + } + return i; + } + } + } + } + return -1; +} + +void Downloads_Destroy( HWND hwndDlg ) +{ + downloads_window = 0; + downloadsSourceWidth = downloadList.GetColumnWidth( COL_SOURCE ); + downloadsTitleWidth = downloadList.GetColumnWidth( COL_TITLE ); + downloadsProgressWidth = downloadList.GetColumnWidth( COL_PROGRESS ); + downloadsPathWidth = downloadList.GetColumnWidth( COL_PATH ); + downloadsDateWidth = downloadList.GetColumnWidth( COL_DATE ); + downloadsSizeWidth = downloadList.GetColumnWidth( COL_SIZE ); + + for ( DownloadListItem *l_content : listContents ) + delete l_content; + + listContents.clear(); + + downloadList.setwnd( NULL ); + + bool fAscending; + downloadsItemSort = Downloads_GetListSortColumn( hwndDlg, IDC_DOWNLOADLIST, &fAscending ); + downloadsSortAscending = ( -1 != downloadsItemSort ) ? ( FALSE != fAscending ) : true; + + int activeDownloads = 0; + int historyDownloads = 0; + { + Nullsoft::Utility::AutoLock historylock( downloadedFiles.downloadedLock ); + Nullsoft::Utility::AutoLock statuslock( downloadStatus.statusLock ); + historyDownloads = (int)downloadedFiles.downloadList.size(); + activeDownloads = (int)downloadStatus.downloads.size(); + } + + if ( !activeDownloads && !historyDownloads && !no_auto_hide ) + { + HNAVITEM hItem = MLNavCtrl_FindItemById( plugin.hwndLibraryParent, downloads_treeItem ); + if ( hItem ) + { + MLNavCtrl_DeleteItem( plugin.hwndLibraryParent, hItem ); + downloads_treeItem = 0; + } + } +} + +void Downloads_Remove( bool del = false, HWND parent = NULL ) +{ + int d = -1; + int r = 0; + while ( GetDownload( d ) ) + { + int download = d - r; + DownloadListItem *item = listContents[ download ]; + if ( item->f ) + { + AutoLock lock( downloadedFiles ); + int j = 0; + for ( DownloadList::iterator i = downloadedFiles.begin(); i != downloadedFiles.end(); ++i ) + { + if ( !_wcsicmp( i->path, item->f->path ) ) + { + if ( del ) + { + if ( !downloadedFiles.RemoveAndDelete( j ) ) + MessageBox( parent, WASABI_API_LNGSTRINGW( IDS_DELETEFAILED ), downloadedFiles.downloadList[ j ].path, 0 ); + } + else + downloadedFiles.Remove( j ); + + delete item; + listContents.erase( listContents.begin() + download ); + r++; + dirty++; + + break; + } + + ++j; + } + } + else if ( item->token ) + { + AutoLock lock( downloadStatus.statusLock ); + downloadStatus.downloads[ item->token ].killswitch = 1; + delete item; + listContents.erase( listContents.begin() + download ); + r++; + } + else + { + delete item; + listContents.erase( listContents.begin() + download ); + r++; + } + } + + downloadList.SetVirtualCountAsync( (int)listContents.size() ); + downloadList.UnselectAll(); + // Navigation_ShowService(SERVICE_DOWNLOADS, SHOWMODE_AUTO); +} + +void Downloads_Delete( HWND parent ) +{ + wchar_t message[ 256 ] = { 0 }; + int c = downloadList.GetSelectedCount(); + + if ( !c ) + return; + else if ( c == 1 ) + WASABI_API_LNGSTRINGW_BUF( IDS_PERM_DELETE_ARE_YOU_SURE, message, 256 ); + else + StringCchPrintf( message, 256, WASABI_API_LNGSTRINGW( IDS_PERM_DELETE_THESE_ARE_YOU_SURE ), c ); + + if ( MessageBox( NULL, message, WASABI_API_LNGSTRINGW( IDS_DELETION ), MB_ICONWARNING | MB_YESNO ) == IDNO ) + return; + + Downloads_Remove( true, parent ); +} + +void Downloads_CleanUp(HWND hwndDlg) +{ + wchar_t titleStr[64] = {0}; + if ( MessageBox( hwndDlg, WASABI_API_LNGSTRINGW( IDS_CLEAR_ALL_FINISHED_DOWNLOADS ), WASABI_API_LNGSTRINGW_BUF( IDS_CLEAN_UP_LIST, titleStr, 64 ), MB_ICONWARNING | MB_YESNO ) == IDNO ) + return; + + { + AutoLock lock( downloadedFiles ); + downloadedFiles.downloadList.clear(); + } + + dirty++; + + DownloadsUpdated(); +} + +void Downloads_InfoBox( HWND parent ) +{ + int download = -1; + if ( GetDownload( download ) ) + { + const wchar_t *fn; + if ( listContents[ download ]->f ) + fn = listContents[ download ]->f->path; + else + fn = listContents[ download ]->path; + + if ( fn ) + { + infoBoxParamW p = { parent, fn }; + SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&p, IPC_INFOBOXW ); + } + } +} + +void Downloads_SelectAll() +{ + int l = downloadList.GetCount(); + for ( int i = 0; i < l; i++ ) + downloadList.SetSelected( i ); +} + +static void exploreItemFolder( HWND hwndDlg ) +{ + if ( downloadList.GetSelectionMark() >= 0 ) + { + int download = -1; + while ( GetDownload( download ) ) + { + wchar_t *file; + if ( listContents[ download ]->f ) + file = listContents[ download ]->f->path; + else + file = listContents[ download ]->path; + + WASABI_API_EXPLORERFINDFILE->AddFile( file ); + } + WASABI_API_EXPLORERFINDFILE->ShowFiles(); + } +} + +void Downloads_Cancel() +{ + int l_selected_count = downloadList.GetSelectedCount(); + for ( int i = -1; i < l_selected_count; ++i ) + { + if ( GetDownload( i ) ) + { + dirty++; + if ( !listContents[ i ]->f ) + WAC_API_DOWNLOADMANAGER->CancelDownload( listContents[ i ]->token ); + + break; // Workaround for 5.9.1 to avoid crash if cancel of many downloads in same time + } + } +} + +int we_are_drag_and_dropping = 0; + +static void Downloads_OnColumnClick(HWND hwnd, NMLISTVIEW *plv) +{ + bool fAscending; + INT iSort = Downloads_GetListSortColumn(hwnd, IDC_DOWNLOADLIST, &fAscending); + fAscending = (-1 != iSort && iSort == plv->iSubItem) ? (!fAscending) : true; + Downloads_Sort(hwnd, plv->iSubItem, fAscending); +} + +LRESULT DownloadList_Notify( LPNMHDR l, HWND hwndDlg ) +{ + switch ( l->code ) + { + case LVN_COLUMNCLICK: + Downloads_OnColumnClick( hwndDlg, (NMLISTVIEW *)l ); + break; + case NM_DBLCLK: + Downloads_Play( ( ( !!( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) ) ^ ML_ENQDEF_VAL() ) ); + break; + case LVN_BEGINDRAG: + we_are_drag_and_dropping = 1; + SetCapture( hwndDlg ); + break; + case LVN_ITEMCHANGED: + Downloads_ManageButtons( hwndDlg ); + break; + case LVN_GETDISPINFO: + NMLVDISPINFO *lpdi = (NMLVDISPINFO *)l; + size_t item = lpdi->item.iItem; + + if ( item < 0 || item >= listContents.size() ) + return 0; + + //if (FALSE == downloadsSortAscending) item = listContents.size() - item - 1; + + DownloadListItem *l = listContents[ item ]; + + if ( lpdi->item.mask & LVIF_TEXT ) + { + lpdi->item.pszText[ 0 ] = 0; + switch ( lpdi->item.iSubItem ) + { + case COL_TITLE: + if ( !l->token && l->f ) + { + wchar_t *l_title = L""; + + if ( l->f->title != NULL ) + l_title = l->f->title; + + lstrcpyn( lpdi->item.pszText, l_title, lpdi->item.cchTextMax ); + } + else + { + if ( l->title ) lstrcpyn( lpdi->item.pszText, l->title, lpdi->item.cchTextMax ); + } + break; + case COL_PROGRESS: + if ( !l->token && l->f ) + { + switch ( l->f->downloadStatus ) + { + case DownloadedFile::DOWNLOAD_SUCCESS: + WASABI_API_LNGSTRINGW_BUF( IDS_DOWNLOAD_SUCCESS, lpdi->item.pszText, lpdi->item.cchTextMax ); + break; + case DownloadedFile::DOWNLOAD_FAILURE: + WASABI_API_LNGSTRINGW_BUF( IDS_DOWNLOAD_FAILURE, lpdi->item.pszText, lpdi->item.cchTextMax ); + break; + case DownloadedFile::DOWNLOAD_CANCELED: + WASABI_API_LNGSTRINGW_BUF( IDS_DOWNLOAD_CANCELED, lpdi->item.pszText, lpdi->item.cchTextMax ); + break; + } + } + else lstrcpyn( lpdi->item.pszText, l->status, lpdi->item.cchTextMax ); + break; + case COL_DATE: + { + if ( !l->token && l->f && l->f->downloadDate ) + { + wchar_t tmp[ 128 ] = { 0 }; + MakeDateString( l->f->downloadDate, tmp, 128 ); + lstrcpyn( lpdi->item.pszText, tmp, lpdi->item.cchTextMax ); + } + else + { + WASABI_API_LNGSTRINGW_BUF( IDS_N_A, lpdi->item.pszText, lpdi->item.cchTextMax ); + } + break; + } + case COL_SOURCE: + if ( !l->token && l->f ) + { + wchar_t *l_source = L""; + + if ( l->f->source != NULL ) + l_source = l->f->source; + + lstrcpyn( lpdi->item.pszText, l_source, lpdi->item.cchTextMax ); + } + else + lstrcpyn( lpdi->item.pszText, l->source, lpdi->item.cchTextMax ); + break; + case COL_SIZE: + { + if ( !l->token && l->f && l->f->totalSize > 0 ) + WASABI_API_LNG->FormattedSizeString( lpdi->item.pszText, lpdi->item.cchTextMax, l->f->totalSize ); + else + WASABI_API_LNGSTRINGW_BUF( IDS_N_A, lpdi->item.pszText, lpdi->item.cchTextMax ); + break; + } + case COL_PATH: + if ( !l->token && l->f ) + { + wchar_t *l_path = L""; + + if ( l->f->path != NULL ) + l_path = l->f->path; + + lstrcpyn( lpdi->item.pszText, l_path, lpdi->item.cchTextMax ); + } + else + { + if ( l->path ) + lstrcpyn( lpdi->item.pszText, l->path, lpdi->item.cchTextMax ); + } + + break; + } + } + + break; + } + + return 0; +} + +void listbuild( wchar_t **buf, int &buf_size, int &buf_pos, const wchar_t *tbuf ) +{ + if ( !*buf ) + { + *buf = (wchar_t *)calloc( 4096, sizeof( wchar_t ) ); + if ( *buf ) + { + buf_size = 4096; + buf_pos = 0; + } + else + { + buf_size = buf_pos = 0; + } + } + int newsize = buf_pos + lstrlenW( tbuf ) + 1; + if ( newsize < buf_size ) + { + size_t old_buf_size = buf_size; + buf_size = newsize + 4096; + wchar_t *new_buf = (wchar_t *)realloc( *buf, ( buf_size + 1 ) * sizeof( wchar_t ) ); + if ( new_buf ) + { + *buf = new_buf; + } + else + { + new_buf = (wchar_t*)calloc( ( buf_size + 1 ), sizeof( wchar_t ) ); + if ( new_buf ) + { + memcpy( new_buf, *buf, ( old_buf_size * sizeof( wchar_t ) ) ); + free( *buf ); + *buf = new_buf; + } + else buf_size = (int)old_buf_size; + } + } + + StringCchCopyW( *buf + buf_pos, buf_size, tbuf ); + buf_pos = newsize; +} + +wchar_t *getSelectedList() +{ + wchar_t *path = NULL; + int buf_pos = 0; + int buf_size = 0; + int download = -1; + + while ( GetDownload( download ) ) + { + if ( listContents[ download ]->f ) + listbuild( &path, buf_size, buf_pos, listContents[ download ]->f->path ); + } + + if ( path ) + path[ buf_pos ] = 0; + + return path; +} + +static void SwapPlayEnqueueInMenu( HMENU listMenu ) +{ + int playPos = -1, enqueuePos = -1; + MENUITEMINFOW playItem = { sizeof( MENUITEMINFOW ), 0, }, enqueueItem = { sizeof( MENUITEMINFOW ), 0, }; + + int numItems = GetMenuItemCount( listMenu ); + + for ( int i = 0; i < numItems; i++ ) + { + UINT id = GetMenuItemID( listMenu, i ); + if ( id == IDC_PLAY ) + { + playItem.fMask = MIIM_ID; + playPos = i; + GetMenuItemInfoW( listMenu, i, TRUE, &playItem ); + } + else if ( id == IDC_ENQUEUE ) + { + enqueueItem.fMask = MIIM_ID; + enqueuePos = i; + GetMenuItemInfoW( listMenu, i, TRUE, &enqueueItem ); + } + } + + playItem.wID = IDC_ENQUEUE; + enqueueItem.wID = IDC_PLAY; + SetMenuItemInfoW( listMenu, playPos, TRUE, &playItem ); + SetMenuItemInfoW( listMenu, enqueuePos, TRUE, &enqueueItem ); +} + +static void SyncMenuWithAccelerators( HWND hwndDlg, HMENU menu ) +{ + HACCEL szAccel[ 24 ] = { 0 }; + INT c = WASABI_API_APP->app_getAccelerators( hwndDlg, szAccel, sizeof( szAccel ) / sizeof( szAccel[ 0 ] ), FALSE ); + AppendMenuShortcuts( menu, szAccel, c, MSF_REPLACE ); +} + +void UpdateMenuItems( HWND hwndDlg, HMENU menu ) +{ + bool swapPlayEnqueue = false; + if ( ML_ENQDEF_VAL() ) + { + SwapPlayEnqueueInMenu( menu ); + swapPlayEnqueue = true; + } + + SyncMenuWithAccelerators( hwndDlg, menu ); + if ( swapPlayEnqueue ) + SwapPlayEnqueueInMenu( menu ); +} + +static int IPC_LIBRARY_SENDTOMENU = 0; +static librarySendToMenuStruct s = { 0 }; + +static void DownloadList_RightClick(HWND hwndDlg, HWND listHwnd, POINTS pts) +{ + POINT pt; + POINTSTOPOINT(pt, pts); + + RECT controlRect, headerRect; + if (FALSE == GetClientRect(listHwnd, &controlRect)) + SetRectEmpty(&controlRect); + else + MapWindowPoints(listHwnd, HWND_DESKTOP, (POINT*)&controlRect, 2); + + if ( -1 == pt.x && -1 == pt.y ) + { + RECT itemRect; + int selected = downloadList.GetNextSelected(); + if ( selected != -1 ) // if something is selected we'll drop the menu from there + { + downloadList.GetItemRect( selected, &itemRect ); + ClientToScreen( listHwnd, (POINT *)&itemRect ); + } + else // otherwise we'll drop it from the top-left corner of the listview, adjusting for the header location + { + GetWindowRect( listHwnd, &itemRect ); + + HWND hHeader = (HWND)SNDMSG( listHwnd, LVM_GETHEADER, 0, 0L ); + RECT headerRect; + if ( ( WS_VISIBLE & GetWindowLongPtr( hHeader, GWL_STYLE ) ) && GetWindowRect( hHeader, &headerRect ) ) + { + itemRect.top += ( headerRect.bottom - headerRect.top ); + } + } + + pt.x = itemRect.left; + pt.y = itemRect.top; + } + + HWND hHeader = (HWND)SNDMSG(listHwnd, LVM_GETHEADER, 0, 0L); + if ( 0 == ( WS_VISIBLE & GetWindowLongPtr( hHeader, GWL_STYLE ) ) || FALSE == GetWindowRect( hHeader, &headerRect ) ) + { + SetRectEmpty( &headerRect ); + } + + if ( FALSE != PtInRect( &headerRect, pt ) ) + { + return; + } + + LVHITTESTINFO hitTest; + hitTest.pt = pt; + MapWindowPoints( HWND_DESKTOP, listHwnd, &hitTest.pt, 1 ); + + int index = ( downloadList.GetNextSelected() != -1 ? ListView_HitTest( listHwnd, &hitTest ) : -1 ); + + HMENU baseMenu = WASABI_API_LOADMENU( IDR_MENU1 ); + + if ( baseMenu == NULL ) + return; + + HMENU menu = GetSubMenu( baseMenu, 0 ); + if ( menu != NULL ) + { + if ( ( index == -1 ) || ( index != -1 ) && listContents[ index ]->f ) + DeleteMenu( menu, ID_DOWNLOADS_CANCELDOWNLOAD, MF_BYCOMMAND ); + + UINT enableExtras = MF_ENABLED; + if ( ( index == -1 ) || ( index != -1 ) && !listContents[ index ]->f + || ( index != -1 ) && ( listContents[ index ]->f->downloadStatus != 1 ) ) + enableExtras = ( MF_GRAYED | MF_DISABLED ); + + UINT enableViewExtras = MF_ENABLED; + if ( index == -1 ) + enableViewExtras = ( MF_GRAYED | MF_DISABLED ); + + EnableMenuItem( menu, IDC_PLAY, MF_BYCOMMAND | enableExtras ); + EnableMenuItem( menu, IDC_ENQUEUE, MF_BYCOMMAND | enableExtras ); + EnableMenuItem( menu, IDC_REMOVE, MF_BYCOMMAND | enableViewExtras ); + EnableMenuItem( menu, IDC_DELETE, MF_BYCOMMAND | enableExtras ); + EnableMenuItem( menu, IDC_INFOBOX, MF_BYCOMMAND | enableExtras ); + EnableMenuItem( menu, ID_DOWNLOADS_EXPLORERITEMFOLDER, MF_BYCOMMAND | enableExtras ); + EnableMenuItem( menu, 2, MF_BYPOSITION | enableExtras ); + + { // send-to menu shit... + ZeroMemory( &s, sizeof( s ) ); + IPC_LIBRARY_SENDTOMENU = (int)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, ( WPARAM ) & "LibrarySendToMenu", IPC_REGISTER_WINAMP_IPCMESSAGE ); + if ( IPC_LIBRARY_SENDTOMENU > 65536 && SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)0, IPC_LIBRARY_SENDTOMENU ) == 0xffffffff ) + { + s.mode = 1; + s.hwnd = hwndDlg; + s.data_type = ML_TYPE_FILENAMESW; + s.ctx[ 1 ] = 1; + s.build_hMenu = GetSubMenu( menu, 2 ); + } + } + + UpdateMenuItems( hwndDlg, menu ); + + int r = Menu_TrackPopup( plugin.hwndLibraryParent, menu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, pt.x, pt.y, hwndDlg, NULL ); + if ( !SendMessage( hwndDlg, WM_COMMAND, r, 0 ) ) + { + s.menu_id = r; // more send to menu shit... + if ( s.mode == 2 && SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU ) == 0xffffffff ) + { + s.mode = 3; + wchar_t *path = getSelectedList(); + if ( path ) + { + s.data = path; + SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU ); + free( path ); + } + } + } + + if ( s.mode ) + { // yet more send to menu shit... + s.mode = 4; + SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU ); // cleanup + } + } + + DestroyMenu( baseMenu ); +} + +static void Downloads_ContextMenu( HWND hwndDlg, WPARAM wParam, LPARAM lParam ) +{ + HWND sourceWindow = (HWND)wParam; + if ( sourceWindow == downloadList.getwnd() ) + DownloadList_RightClick( hwndDlg, sourceWindow, MAKEPOINTS( lParam ) ); +} + +enum +{ + BPM_ECHO_WM_COMMAND=0x1, // send WM_COMMAND and return value + BPM_WM_COMMAND = 0x2, // just send WM_COMMAND +}; + +BOOL Downloads_ButtonPopupMenu( HWND hwndDlg, int buttonId, HMENU menu, int flags = 0 ) +{ + RECT r; + HWND buttonHWND = GetDlgItem( hwndDlg, buttonId ); + GetWindowRect( buttonHWND, &r ); + UpdateMenuItems( hwndDlg, menu ); + MLSkinnedButton_SetDropDownState( buttonHWND, TRUE ); + UINT tpmFlags = TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_BOTTOMALIGN | TPM_LEFTALIGN; + if ( !( flags & BPM_WM_COMMAND ) ) + tpmFlags |= TPM_RETURNCMD; + int x = Menu_TrackPopup( plugin.hwndLibraryParent, menu, tpmFlags, r.left, r.top, hwndDlg, NULL ); + if ( ( flags & BPM_ECHO_WM_COMMAND ) && x ) + SendMessage( hwndDlg, WM_COMMAND, MAKEWPARAM( x, 0 ), 0 ); + MLSkinnedButton_SetDropDownState( buttonHWND, FALSE ); + return x; +} + +static void Downloads_Play( HWND hwndDlg, HWND from, UINT idFrom ) +{ + HMENU listMenu = GetSubMenu( g_context_menus2, 0 ); + int count = GetMenuItemCount( listMenu ); + if ( count > 2 ) + { + for ( int i = 2; i < count; i++ ) + { + DeleteMenu( listMenu, 2, MF_BYPOSITION ); + } + } + + Downloads_ButtonPopupMenu( hwndDlg, idFrom, listMenu, BPM_WM_COMMAND ); +} + +static BOOL WINAPI DownloadDialog_DlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITMENUPOPUP: // yet yet more send to menu shit... + if (wParam && (HMENU)wParam == s.build_hMenu && s.mode==1) + { + if (SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&s,IPC_LIBRARY_SENDTOMENU)==0xffffffff) + s.mode=2; + } + break; + + case WM_USER+543: + Navigation_Update(); + break; + + case WM_CONTEXTMENU: + Downloads_ContextMenu(hwndDlg, wParam, lParam); + return TRUE; + + case WM_NOTIFYFORMAT: + return NFR_UNICODE; + + case WM_INITDIALOG: + Downloads_Init(hwndDlg); + break; + + case WM_NOTIFY: + { + LPNMHDR l = (LPNMHDR)lParam; + if (l->idFrom == IDC_DOWNLOADLIST) + return (BOOL)DownloadList_Notify(l,hwndDlg); + } + break; + + case WM_DESTROY: + Downloads_Destroy(hwndDlg); + return 0; + + case WM_DISPLAYCHANGE: + Downloads_DisplayChange(hwndDlg); + return 0; + + case WM_TIMER: + Downloads_Timer(hwndDlg, (UINT)wParam); + break; + + case WM_MOUSEMOVE: + if (we_are_drag_and_dropping && GetCapture() == hwndDlg) + { + POINT p = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + ClientToScreen(hwndDlg, &p); + mlDropItemStruct m; + ZeroMemory(&m, sizeof(mlDropItemStruct)); + m.type = ML_TYPE_FILENAMESW; + m.p = p; + SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDRAG); + } + break; + + case WM_LBUTTONUP: + if (we_are_drag_and_dropping && GetCapture() == hwndDlg) + { + we_are_drag_and_dropping = 0; + ReleaseCapture(); + POINT p = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + ClientToScreen(hwndDlg, &p); + mlDropItemStruct m = {0}; + m.type = ML_TYPE_FILENAMESW; + m.p = p; + m.flags = ML_HANDLEDRAG_FLAG_NOCURSOR; + SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDRAG); + if (m.result > 0) + { + m.flags = 0; + m.result = 0; + wchar_t* path = getSelectedList(); + if(path) + { + m.data = path; + SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDROP); + free(path); + } + } + } + break; + + case WM_PAINT: + { + int tab[] = { IDC_DOWNLOADLIST|DCW_SUNKENBORDER}; + dialogSkinner.Draw(hwndDlg, tab, sizeof(tab) / sizeof(tab[0])); + } + 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_APP + 104: + { + Downloads_UpdateButtonText(hwndDlg, (int)wParam); + LayoutWindows(hwndDlg, TRUE); + return 0; + } + + case WM_COMMAND: + switch ( LOWORD( wParam ) ) + { + case IDC_PLAY: + case IDC_ENQUEUE: + case IDC_CUSTOM: + if ( HIWORD( wParam ) == MLBN_DROPDOWN ) + { + Downloads_Play( hwndDlg, (HWND)lParam, LOWORD( wParam ) ); + } + else + { + bool action; + if ( LOWORD( wParam ) == IDC_PLAY ) + { + action = ( HIWORD( wParam ) == 1 ) ? ML_ENQDEF_VAL() == 1 : 0; + } + else if ( LOWORD( wParam ) == IDC_ENQUEUE ) + { + action = ( HIWORD( wParam ) == 1 ) ? ML_ENQDEF_VAL() != 1 : 1; + } + else + break; + + Downloads_Play( action ); + } + break; + case IDC_REMOVE: + Downloads_Remove(); + break; + case IDC_DELETE: + Downloads_Delete( hwndDlg ); + break; + case IDC_CLEANUP: + Downloads_CleanUp( hwndDlg ); + break; + case IDC_INFOBOX: + Downloads_InfoBox( hwndDlg ); + break; + case IDC_SELECTALL: + Downloads_SelectAll(); + break; + case ID_DOWNLOADS_EXPLORERITEMFOLDER: + exploreItemFolder( hwndDlg ); + break; + case ID_DOWNLOADS_CANCELDOWNLOAD: + Downloads_Cancel(); + break; + default: + return 0; + } + return 1; + } + return 0; +} + +HWND CALLBACK DownloadDialog_Create( HWND hParent ) +{ + return WASABI_API_CREATEDIALOGPARAMW( IDD_DOWNLOADS, hParent, DownloadDialog_DlgProc, 0 ); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/DownloadsDialog.h b/Src/Plugins/Library/ml_downloads/DownloadsDialog.h new file mode 100644 index 00000000..080f6bb6 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/DownloadsDialog.h @@ -0,0 +1,11 @@ +#ifndef NULLSOFT_DOWNLOADSDIALOGH +#define NULLSOFT_DOWNLOADSDIALOGH + +#include "DownloadStatus.h" +#include "Downloaded.h" + +void DownloadsUpdated(); +void DownloadsUpdated( DownloadToken token, const DownloadedFile *f ); +void DownloadsUpdated( const DownloadStatus::Status &s, DownloadToken token ); + +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/DownloadsParse.cpp b/Src/Plugins/Library/ml_downloads/DownloadsParse.cpp new file mode 100644 index 00000000..627fb52b --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/DownloadsParse.cpp @@ -0,0 +1,179 @@ +#include "Main.h" +#include "DownloadsParse.h" +#include "Downloaded.h" +#include "Defaults.h" +#include "ParseUtil.h" +#include <wchar.h> +#include <locale.h> + +static __time64_t filetime( const wchar_t *file ) +{ + if ( !file || !*file ) + return 0; + + WIN32_FIND_DATAW f = { 0 }; + + HANDLE h = FindFirstFileW( file, &f ); + if ( h == INVALID_HANDLE_VALUE ) + { + /*wchar_t tmp[128] = {0}; + wsprintf(tmp,L"%d",GetLastError()); + MessageBox(0,file,tmp,0);*/ + return 0; + } + + FindClose( h ); + SYSTEMTIME s = { 0 }; + FileTimeToSystemTime( &f.ftCreationTime, &s ); + + tm t = { 0 }; + t.tm_year = s.wYear - 1900; + t.tm_mon = s.wMonth - 1; + t.tm_mday = s.wDay; + t.tm_hour = s.wHour; + t.tm_min = s.wMinute; + t.tm_sec = s.wMinute; + __time64_t ret = _mktime64(&t); + /*wchar_t tmp[128] = {0}; + wsprintf(tmp,L"%d",ret); + MessageBox(0,tmp,0,0);*/ + + return ret; +} + +size_t GetFileSize(LPCWSTR path) +{ + WIN32_FIND_DATAW data = {0}; + if (path && *path) + { + HANDLE h = FindFirstFileW(path, &data); + if (h == INVALID_HANDLE_VALUE) + return -1; + FindClose(h); + } + return data.nFileSizeLow/* | (__int64)data.nFileSizeHigh << 32*/; +} + +static void ReadDownload( const XMLNode *item ) +{ + DownloadedFile newDownloaded; + + const wchar_t *source = GetContent( item, L"source" ); + if ( !source ) source = GetContent( item, L"channel" ); + newDownloaded.SetSource( source ); + + const wchar_t *title = GetContent( item, L"title" ); + if ( !title ) title = GetContent( item, L"item" );; + newDownloaded.SetTitle( title ); + + const wchar_t *url = GetContent( item, L"url" ); + newDownloaded.SetURL( url ); + + const wchar_t *path = GetContent( item, L"path" ); + newDownloaded.SetPath( path ); + + // TODO ideally should be able to cope with __int64 + // but the db is setup for int currently... + const wchar_t *size = GetContent( item, L"size" ); + if ( size && size[ 0 ] ) + { + size_t val = _wtoi( size ); + if ( val > 0 ) newDownloaded.totalSize = val; + else if ( !val ) + { + val = GetFileSize( path ); + if ( val > 0 ) newDownloaded.totalSize = val; + else newDownloaded.totalSize = -1; + dirty++; + } + } + else + { + size_t val = GetFileSize( path ); + if ( val > 0 ) newDownloaded.totalSize = val; + else newDownloaded.totalSize = -1; + dirty++; + } + + const wchar_t *downloadDate = GetContent( item, L"downloadDate" ); + if ( downloadDate && downloadDate[ 0 ] ) + { + __time64_t val = (__time64_t)_wtoi( downloadDate ); + if ( time > 0 ) newDownloaded.downloadDate = val; + } + else + { + __time64_t val = filetime( newDownloaded.path ); + if ( time > 0 ) newDownloaded.downloadDate = val; + } + + const wchar_t *status = GetContent( item, L"downloadStatus" ); + if ( status && status[ 0 ] ) + { + newDownloaded.downloadStatus = _wtoi( status ); + } + else + { + newDownloaded.downloadStatus = DownloadedFile::DOWNLOAD_SUCCESS; + } + + downloadedFiles.downloadList.push_back( newDownloaded ); +} + +void DownloadsParse::ReadNodes( const wchar_t *url ) +{ + XMLNode::NodeList::const_iterator itr; + const XMLNode *curNode = xmlDOM.GetRoot(); + + if ( curNode->Present( L"winamp:preferences" ) ) + curNode = curNode->Get( L"winamp:preferences" ); + + curNode = curNode->Get( L"downloads" ); + if ( curNode ) + { + const wchar_t *prop = curNode->GetProperty( L"downloadsTitleWidth" ); + if ( prop && prop[ 0 ] ) + downloadsTitleWidth = _wtoi( prop ); + if ( downloadsTitleWidth <= 0 ) + downloadsTitleWidth = DOWNLOADSTITLEWIDTHDEFAULT; + + prop = curNode->GetProperty( L"downloadsProgressWidth" ); + if ( prop && prop[ 0 ] ) + downloadsProgressWidth = _wtoi( prop ); + if ( downloadsProgressWidth <= 0 ) + downloadsProgressWidth = DOWNLOADSPROGRESSWIDTHDEFAULT; + + prop = curNode->GetProperty( L"downloadsDateWidth" ); + if ( prop && prop[ 0 ] ) + downloadsDateWidth = _wtoi( prop ); + if ( downloadsDateWidth <= 0 ) + downloadsDateWidth = DOWNLOADSDATEWIDTHDEFAULTS; + + prop = curNode->GetProperty( L"downloadsSourceWidth" ); + if ( prop && prop[ 0 ] ) + downloadsSourceWidth = _wtoi( prop ); + if ( downloadsSourceWidth <= 0 ) + downloadsSourceWidth = DOWNLOADSSOURCEWIDTHDEFAULT; + + prop = curNode->GetProperty( L"downloadsPathWidth" ); + if ( prop && prop[ 0 ] ) + downloadsPathWidth = _wtoi( prop ); + if ( downloadsPathWidth <= 0 ) + downloadsPathWidth = DOWNLOADSPATHWIDTHDEFAULTS; + + prop = curNode->GetProperty( L"downloadsItemSort" ); + if ( prop && prop[ 0 ] ) + downloadsItemSort = _wtoi( prop ); + + prop = curNode->GetProperty( L"downloadsSortAscending" ); + if ( prop && prop[ 0 ] ) + downloadsSortAscending = !PropertyIsFalse( curNode, L"downloadsSortAscending" ); + + const XMLNode::NodeList *downloadsList = curNode->GetList( L"download" ); + if ( downloadsList ) + { + for ( itr = downloadsList->begin(); itr != downloadsList->end(); itr++ ) + ReadDownload( *itr ); + } + } +} diff --git a/Src/Plugins/Library/ml_downloads/DownloadsParse.h b/Src/Plugins/Library/ml_downloads/DownloadsParse.h new file mode 100644 index 00000000..8f70ceb9 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/DownloadsParse.h @@ -0,0 +1,13 @@ +#ifndef NULLSOFT_DOWNLOADSPARSEH +#define NULLSOFT_DOWNLOADSPARSEH + +#include "DownloadThread.h" + +class DownloadsParse : public DownloadThread +{ +public: + virtual void ReadNodes(const wchar_t *url); + +}; + +#endif diff --git a/Src/Plugins/Library/ml_downloads/Main.cpp b/Src/Plugins/Library/ml_downloads/Main.cpp new file mode 100644 index 00000000..09a260eb --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/Main.cpp @@ -0,0 +1,387 @@ +#include "main.h" +#include "DownloadsDialog.h" +#include "DownloadsParse.h" +#include "Preferences.h" +#include "XMLWriter.h" +#include "..\..\General\gen_ml/ml.h" +#include "Defaults.h" +#include "..\..\General\gen_ml/ml_ipc_0313.h" + +#include "api__ml_downloads.h" +#include "Downloaded.h" +#include "DownloadStatus.h" +#include <api/service/waServiceFactory.h> + +#include <strsafe.h> + +wchar_t *ml_cfg = 0, + xmlFileName[1024] = {0}; + +ATOM VIEWPROP = 0; + +api_downloadManager *WAC_API_DOWNLOADMANAGER = 0; +DownloadViewCallback *downloadViewCallback = 0; + +static int Init(); +static void Quit(); +static INT_PTR MessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3); + +DWORD threadStorage=TLS_OUT_OF_INDEXES; +extern "C" winampMediaLibraryPlugin plugin = + { + MLHDR_VER, + "nullsoft(ml_downloads.dll)", + Init, + Quit, + MessageProc, + 0, + 0, + 0, + }; + +//static prefsDlgRecW preferences; +//static wchar_t preferencesName[64] = {0}; +int downloads_treeItem = 0, + podcast_parent = 0, + no_auto_hide = 0; +HANDLE hMainThread; + +HCURSOR hDragNDropCursor; +int winampVersion = 0, dirty = 0; + +api_application *applicationApi = NULL; +api_explorerfindfile *WASABI_API_EXPLORERFINDFILE = 0; + +// wasabi based services for localisation support +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +int icons[4] = {-1, -1, -1, -1}; +int activeIcon = 0; +int totalIcons = 4; +static Nullsoft::Utility::LockGuard navigationLock; + +// used to get the downloads view parented to the podcast view +#define CSTR_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) +#define NAVITEM_PREFIX L"podcast_svc_" +#define SERVICE_PODCAST 720 + +enum +{ + DOWNLOADS_TIMER_NAVNODE = 35784, +}; + +HNAVITEM Navigation_FindService(UINT serviceId) +{ + HWND hLibrary = plugin.hwndLibraryParent; + INT cchPrefix = ARRAYSIZE(NAVITEM_PREFIX) - 1; + + WCHAR szBuffer[256] = {0}; + NAVITEM itemInfo = {0}; + itemInfo.cbSize = sizeof(itemInfo); + itemInfo.mask = NIMF_TEXTINVARIANT; + itemInfo.cchInvariantMax = ARRAYSIZE(szBuffer); + itemInfo.pszInvariant = szBuffer; + itemInfo.hItem = MLNavCtrl_GetFirst(hLibrary); + + while(NULL != itemInfo.hItem) + { + if (FALSE != MLNavItem_GetInfo(hLibrary, &itemInfo) && + CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, itemInfo.pszInvariant, cchPrefix, + NAVITEM_PREFIX, cchPrefix)) + { + return itemInfo.hItem; + } + itemInfo.hItem = MLNavItem_GetNext(hLibrary, itemInfo.hItem); + } + return NULL; +} + +void Navigation_Update_Icon(void) +{ + Nullsoft::Utility::AutoLock navlock(navigationLock); + + HNAVITEM hItem = NULL; + if(downloads_treeItem) + { + hItem = MLNavCtrl_FindItemById(plugin.hwndLibraryParent,downloads_treeItem); + } + + if (hItem) + { + NAVITEM item; + item.cbSize = sizeof(NAVITEM); + item.hItem = hItem; + item.iSelectedImage = item.iImage = icons[activeIcon]; + activeIcon = (activeIcon + 1) % totalIcons; + item.mask = NIMF_IMAGE | NIMF_IMAGESEL; + MLNavItem_SetInfo(plugin.hwndLibraryParent, &item); + } +} + +BOOL Navigation_Update(void) +{ + int activeDownloads = 0; + int historyDownloads = 0; + { + Nullsoft::Utility::AutoLock historylock(downloadedFiles.downloadedLock); + historyDownloads = (int)downloadedFiles.downloadList.size(); + } + + { + Nullsoft::Utility::AutoLock statuslock(downloadStatus.statusLock); + activeDownloads = (int)downloadStatus.downloads.size(); + } + + Nullsoft::Utility::AutoLock navlock(navigationLock); + HNAVITEM hItem = NULL; + if (downloads_treeItem) + { + hItem = MLNavCtrl_FindItemById(plugin.hwndLibraryParent,downloads_treeItem); + } + + NAVINSERTSTRUCT nis = {0}; + nis.item.cbSize = sizeof(NAVITEM); + nis.item.pszText = WASABI_API_LNGSTRINGW(IDS_DOWNLOADS); + nis.item.iSelectedImage = nis.item.iImage = icons[0]; + + WCHAR szName[256] = {0}; + if (activeDownloads && SUCCEEDED(StringCchPrintf(szName, ARRAYSIZE(szName), L"%s (%u)", WASABI_API_LNGSTRINGW(IDS_DOWNLOADS), activeDownloads))) + nis.item.pszText = szName; + + if (activeDownloads > 0 || historyDownloads > 0 || no_auto_hide == 2) + { + if (!hItem) + { + nis.item.pszInvariant = NAVITEM_UNIQUESTR; + nis.item.mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_IMAGE | NIMF_IMAGESEL; + if(podcast_parent) nis.hParent = Navigation_FindService(SERVICE_PODCAST); + NAVITEM nvItem = {sizeof(NAVITEM),0,NIMF_ITEMID,}; + + nvItem.hItem = MLNavCtrl_InsertItem(plugin.hwndLibraryParent, &nis); + + MLNavItem_GetInfo(plugin.hwndLibraryParent, &nvItem); + downloads_treeItem = nvItem.id; + } + else + { + nis.item.hItem = hItem; + nis.item.mask = NIMF_TEXT; + MLNavItem_SetInfo(plugin.hwndLibraryParent, &nis.item); + } + } + else if (hItem) + { + nis.item.hItem = hItem; + nis.item.mask = NIMF_TEXT | NIMF_IMAGE | NIMF_IMAGESEL; + MLNavItem_SetInfo(plugin.hwndLibraryParent, &nis.item); + } + + return TRUE; +} + +void CALLBACK Downloads_Nav_Timer(HWND hwnd, UINT uMsg, UINT_PTR eventId, ULONG elapsed) +{ + switch (eventId) + { + case DOWNLOADS_TIMER_NAVNODE: + if (downloadStatus.CurrentlyDownloading()) + Navigation_Update_Icon(); + break; + } +} + + +int Init() +{ + hMainThread = GetCurrentThread(); + hDragNDropCursor = LoadCursor( GetModuleHandle( L"gen_ml.dll" ), MAKEINTRESOURCE( ML_IDC_DRAGDROP ) ); + threadStorage = TlsAlloc(); + + if ( 0 == VIEWPROP ) + { + VIEWPROP = GlobalAddAtom( L"Nullsoft_DownloadsView" ); + if ( VIEWPROP == 0 ) + return ML_INIT_FAILURE; + } + + winampVersion = (int)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETVERSION ); + + waServiceFactory *sf = plugin.service->service_getServiceByGuid( DownloadManagerGUID ); + if ( !sf ) // no sense in continuing + return ML_INIT_FAILURE; + else + WAC_API_DOWNLOADMANAGER = reinterpret_cast<api_downloadManager *>( sf->getInterface() ); + + // loader so that we can get the localisation service api for use + sf = plugin.service->service_getServiceByGuid( languageApiGUID ); + if ( sf ) + WASABI_API_LNG = reinterpret_cast<api_language*>( sf->getInterface() ); + + sf = plugin.service->service_getServiceByGuid( applicationApiServiceGuid ); + if ( sf ) + WASABI_API_APP = reinterpret_cast<api_application*>( sf->getInterface() ); + + sf = plugin.service->service_getServiceByGuid( ExplorerFindFileApiGUID ); + if ( sf ) + WASABI_API_EXPLORERFINDFILE = reinterpret_cast<api_explorerfindfile*>( sf->getInterface() ); + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG( plugin.hDllInstance, MlDownloadsLangGUID ); + + static wchar_t szDescription[ 256 ]; + StringCchPrintf( szDescription, ARRAYSIZE( szDescription ), WASABI_API_LNGSTRINGW( IDS_PLUGIN_NAME ), PLUGIN_VERSION_MAJOR, PLUGIN_VERSION_MINOR ); + plugin.description = (char*)szDescription; + + mediaLibrary.library = plugin.hwndLibraryParent; + mediaLibrary.winamp = plugin.hwndWinampParent; + mediaLibrary.instance = plugin.hDllInstance; + + BuildDefaults( plugin.hwndWinampParent ); + + mediaLibrary.BuildPath( L"Plugins\\ml\\downloads.xml", xmlFileName, 1024 ); + if ( PathFileExists( xmlFileName ) ) + { + DownloadsParse downloader; + downloader.DownloadFile( xmlFileName ); + } + else + { + wchar_t xmlRssFileName[ 1024 ] = { 0 }; + mediaLibrary.BuildPath( L"Plugins\\ml\\feeds\\rss.xml", xmlRssFileName, 1024 ); + { + DownloadsParse rssDownloader; + rssDownloader.DownloadFile( xmlRssFileName ); + } + } + + icons[ 0 ] = mediaLibrary.AddTreeImageBmp( IDB_TREEITEM_DOWNLOADS ); + icons[ 1 ] = mediaLibrary.AddTreeImageBmp( IDB_TREEITEM_DOWNLOADS1 ); + icons[ 2 ] = mediaLibrary.AddTreeImageBmp( IDB_TREEITEM_DOWNLOADS2 ); + icons[ 3 ] = mediaLibrary.AddTreeImageBmp( IDB_TREEITEM_DOWNLOADS3 ); + + ml_cfg = (wchar_t *)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETMLINIFILEW ); + + podcast_parent = ( !!GetPrivateProfileInt( L"gen_ml_config", L"podcast_parent", 0, ml_cfg ) ); + no_auto_hide = GetPrivateProfileInt( L"gen_ml_config", L"no_auto_hide", 0, ml_cfg ); + + Navigation_Update(); + + downloadViewCallback = new DownloadViewCallback(); + WAC_API_DOWNLOADMANAGER->RegisterStatusCallback( downloadViewCallback ); + + SetTimer( plugin.hwndLibraryParent, DOWNLOADS_TIMER_NAVNODE, 1000, Downloads_Nav_Timer ); + + return ML_INIT_SUCCESS; +} + +void Quit() +{ + KillTimer( plugin.hwndLibraryParent, DOWNLOADS_TIMER_NAVNODE ); + + // If there are still files downloading, cancel download to remove incomplete downloaded files + while ( downloadStatus.CurrentlyDownloading() ) + { + Nullsoft::Utility::AutoLock lock(downloadStatus.statusLock); + DownloadToken dltoken = downloadStatus.downloads.begin()->first; + WAC_API_DOWNLOADMANAGER->CancelDownload(dltoken); + } + + if (dirty) + { + SaveDownloads( xmlFileName, downloadedFiles ); + dirty = 0; + } + + WAC_API_DOWNLOADMANAGER->UnregisterStatusCallback( downloadViewCallback ); + downloadViewCallback->Release(); + + waServiceFactory *sf = plugin.service->service_getServiceByGuid( applicationApiServiceGuid ); + if ( sf != NULL ) + sf->releaseInterface(WASABI_API_APP); + + sf = plugin.service->service_getServiceByGuid(ExplorerFindFileApiGUID); + if ( sf != NULL ) + sf->releaseInterface(WASABI_API_EXPLORERFINDFILE); + + sf = plugin.service->service_getServiceByGuid(DownloadManagerGUID); + if ( sf != NULL ) + sf->releaseInterface(WAC_API_DOWNLOADMANAGER); + + if ( VIEWPROP != 0 ) + { + GlobalDeleteAtom(VIEWPROP); + VIEWPROP = 0; + } +} + +HWND CALLBACK DownloadDialog_Create( HWND hParent ); + +INT_PTR MessageProc( int msg, INT_PTR param1, INT_PTR param2, INT_PTR param3 ) +{ + switch ( msg ) + { + case ML_MSG_DOWNLOADS_VIEW_LOADED: + return TRUE; + + case ML_MSG_DOWNLOADS_VIEW_POSITION: + { + // toggle the position of the node based on the preferences settings + if ( param2 ) + { + podcast_parent = (int)param1; + if ( downloads_treeItem ) + { + HNAVITEM hItem = MLNavCtrl_FindItemById( plugin.hwndLibraryParent, downloads_treeItem ); + if (hItem) + { + if ( MLNavCtrl_DeleteItem( plugin.hwndLibraryParent, hItem ) ) + { + downloads_treeItem = 0; + } + } + } + } + + Navigation_Update(); + return 0; + } + + case ML_MSG_TREE_ONCREATEVIEW: + return ( param1 == downloads_treeItem ) ? (INT_PTR)DownloadDialog_Create( (HWND)param2 ) : 0; + + case ML_MSG_NO_CONFIG: + return TRUE; + + case ML_MSG_NOTOKTOQUIT: + if (downloadStatus.CurrentlyDownloading()) + { + wchar_t titleStr[32] = {0}; + if ( MessageBox( plugin.hwndLibraryParent, WASABI_API_LNGSTRINGW( IDS_CANCEL_DOWNLOADS_AND_QUIT ), WASABI_API_LNGSTRINGW_BUF( IDS_CONFIRM_QUIT, titleStr, 32 ), MB_YESNO | MB_ICONQUESTION ) == IDNO ) + return TRUE; + } + return FALSE; + + case ML_MSG_WRITE_CONFIG: + if ( dirty ) + { + SaveDownloads( xmlFileName, downloadedFiles ); + dirty = 0; + } + break; + + case ML_MSG_VIEW_PLAY_ENQUEUE_CHANGE: + enqueuedef = (int)param1; + groupBtn = (int)param2; + PostMessage( downloads_window, WM_APP + 104, param1, param2 ); + return 0; + } + + return 0; +} + + +extern "C" __declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin() +{ + return &plugin; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/Main.h b/Src/Plugins/Library/ml_downloads/Main.h new file mode 100644 index 00000000..39c4de66 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/Main.h @@ -0,0 +1,43 @@ +#ifndef NULLSOFT_MAINH +#define NULLSOFT_MAINH + +#include "Downloaded.h" + +#define PLUGIN_VERSION_MAJOR 1 +#define PLUGIN_VERSION_MINOR 33 + +extern int winampVersion, podcast_parent, dirty; + +#define NAVITEM_UNIQUESTR L"download_svc" +BOOL Navigation_Update(void); + +bool AddDownloadData(const DownloadedFile &data); +void CloseDatabase(); + +#include "resource.h" +#include "../nu/DialogSkinner.h" +#include "../nu/MediaLibraryInterface.h" +#include "../nu/AutoChar.h" +#include "../nu/AutoWide.h" +#include "../nu/AutoLock.h" +#include <windows.h> +#include <shlwapi.h> + +extern ATOM VIEWPROP; +extern winampMediaLibraryPlugin plugin; +extern int downloads_treeItem; + +#include "../Components/wac_downloadManager/wac_downloadManager_api.h" + +#define ML_ENQDEF_VAL() (!!GetPrivateProfileInt(L"gen_ml_config", L"enqueuedef", 0, ml_cfg)) +#define ML_GROUPBTN_VAL() (!!GetPrivateProfileInt(L"gen_ml_config", L"groupbtn", 1, ml_cfg)) +extern wchar_t* ml_cfg; + +#include "DownloadViewCallback.h" + +extern DownloadViewCallback *downloadViewCallback; + +#endif + +extern HWND downloads_window; +extern int groupBtn, enqueuedef;
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/MessageProcessor.cpp b/Src/Plugins/Library/ml_downloads/MessageProcessor.cpp new file mode 100644 index 00000000..6067965c --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/MessageProcessor.cpp @@ -0,0 +1,7 @@ +#include "main.h" +#include "MessageProcessor.h" + +#define CBCLASS MessageProcessor +START_DISPATCH; +CB(API_MESSAGEPROCESSOR_PROCESS_MESSAGE, ProcessMessage) +END_DISPATCH;
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/MessageProcessor.h b/Src/Plugins/Library/ml_downloads/MessageProcessor.h new file mode 100644 index 00000000..c48e4b8a --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/MessageProcessor.h @@ -0,0 +1,35 @@ +#ifndef NULLSOFT_ML_WIRE_MESSAGEPROCESSOR_H +#define NULLSOFT_ML_WIRE_MESSAGEPROCESSOR_H + +#include <api/application/api_messageprocessor.h> +#include "main.h" +#ifndef WM_FORWARDMSG +#define WM_FORWARDMSG 0x037F +#endif + +class MessageProcessor : public api_messageprocessor +{ +public: + bool ProcessMessage(MSG *msg) + { + + if (msg->message < WM_KEYFIRST || msg->message > WM_KEYLAST) + return false; + + HWND hWndCtl = ::GetFocus(); + + if (IsChild(browserHWND, hWndCtl)) + { + // find a direct child of the dialog from the window that has focus + while(::GetParent(hWndCtl) != browserHWND) + hWndCtl = ::GetParent(hWndCtl); + + if (activeBrowser->translateKey(*msg)) + return true; + } + return false; + } +protected: + RECVS_DISPATCH; +}; +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/ParseUtil.cpp b/Src/Plugins/Library/ml_downloads/ParseUtil.cpp new file mode 100644 index 00000000..0a5c8d57 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/ParseUtil.cpp @@ -0,0 +1,43 @@ +#include "ParseUtil.h" + +bool PropertyIsTrue( const XMLNode *item, const wchar_t *property ) +{ + if ( !item ) + return false; + + const wchar_t *value = item->GetProperty( property ); + if ( !value ) + return false; + + return !_wcsicmp( value, L"true" ); +} + +bool PropertyIsFalse( const XMLNode *item, const wchar_t *property ) +{ + if ( !item ) + return false; + + const wchar_t *value = item->GetProperty( property ); + if ( !value ) + return false; + + return !_wcsicmp( value, L"false" ); +} + +const wchar_t *GetContent( const XMLNode *item, const wchar_t *tag ) +{ + const XMLNode *curNode = item->Get( tag ); + if ( curNode ) + return curNode->GetContent(); + else + return 0; +} + +const wchar_t *GetProperty( const XMLNode *item, const wchar_t *tag, const wchar_t *property ) +{ + const XMLNode *curNode = item->Get( tag ); + if ( curNode ) + return curNode->GetProperty( property ); + else + return 0; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/ParseUtil.h b/Src/Plugins/Library/ml_downloads/ParseUtil.h new file mode 100644 index 00000000..a277d729 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/ParseUtil.h @@ -0,0 +1,8 @@ +#pragma once +#include "../xml/XMLNode.h" +#include <bfc/platform/types.h> +bool PropertyIsTrue(const XMLNode *item, const wchar_t *property); +// not necessarily the opposite of PropertyIsTrue (returns false when field is empty +bool PropertyIsFalse(const XMLNode *item, const wchar_t *property); +const wchar_t *GetContent(const XMLNode *item, const wchar_t *tag); +const wchar_t *GetProperty(const XMLNode *item, const wchar_t *tag, const wchar_t *property);
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/Preferences.cpp b/Src/Plugins/Library/ml_downloads/Preferences.cpp new file mode 100644 index 00000000..fcd3a9cf --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/Preferences.cpp @@ -0,0 +1,77 @@ +#include "main.h" +#include "api__ml_downloads.h" +#include "../winamp/wa_ipc.h" +#include "Defaults.h" +#include <shlobj.h> + +void Preferences_Init(HWND hwndDlg) +{ + SetDlgItemText(hwndDlg, IDC_DOWNLOADLOCATION, defaultDownloadPath); +} + +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 BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) +{ + if(uMsg == BFFM_INITIALIZED) + { + SendMessageW(hwnd, BFFM_SETSELECTIONW, 1, (LPARAM)defaultDownloadPath); + + // this is not nice but it fixes the selection not working correctly on all OSes + EnumChildWindows(hwnd, browseEnumProc, 0); + } + return 0; +} + +void Preferences_Browse(HWND hwndDlg) +{ + wchar_t folder[MAX_PATH] = {0}; + BROWSEINFO browse = {0}; + lstrcpyn(folder, defaultDownloadPath, MAX_PATH); + browse.hwndOwner = hwndDlg; + browse.lpszTitle = WASABI_API_LNGSTRINGW(IDS_CHOOSE_FOLDER); + browse.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE; + browse.lpfn = BrowseCallbackProc; + LPITEMIDLIST itemList = SHBrowseForFolder(&browse); + if (itemList) + { + SHGetPathFromIDList(itemList, folder); + lstrcpyn(defaultDownloadPath, folder, MAX_PATH); + SetWindowText(GetDlgItem(hwndDlg, IDC_DOWNLOADLOCATION), folder); + LPMALLOC malloc; + SHGetMalloc(&malloc); + malloc->Free(itemList); + } +} + + +BOOL CALLBACK PreferencesDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + Preferences_Init(hwndDlg); + break; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_BROWSE: + Preferences_Browse(hwndDlg); + break; + } + break; + } + + return 0; +} diff --git a/Src/Plugins/Library/ml_downloads/Preferences.h b/Src/Plugins/Library/ml_downloads/Preferences.h new file mode 100644 index 00000000..c0615aab --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/Preferences.h @@ -0,0 +1,6 @@ +#ifndef NULLSOFT_PREFERENCESH +#define NULLSOFT_PREFERENCESH + +BOOL CALLBACK PreferencesDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + +#endif diff --git a/Src/Plugins/Library/ml_downloads/RFCDate.cpp b/Src/Plugins/Library/ml_downloads/RFCDate.cpp new file mode 100644 index 00000000..ab57f9af --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/RFCDate.cpp @@ -0,0 +1,216 @@ +#include "main.h" +#include "RFCDate.h" +#include <strsafe.h> + +void MakeDateString(__time64_t convertTime, wchar_t *date_str, size_t len) +{ + SYSTEMTIME sysTime = {0}; + tm *newtime = _localtime64(&convertTime); + + sysTime.wYear = newtime->tm_year + 1900; + sysTime.wMonth = newtime->tm_mon + 1; + sysTime.wDayOfWeek = newtime->tm_wday; + sysTime.wDay = newtime->tm_mday; + sysTime.wHour = newtime->tm_hour; + sysTime.wMinute = newtime->tm_min; + sysTime.wSecond = newtime->tm_sec; + + GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &sysTime, NULL, date_str, (int)len); + +} + +void MakeRFCDateString(__time64_t convertTime, wchar_t *data_str, size_t len) +{ + SYSTEMTIME sysTime = {0}; + tm *newtime = _gmtime64(&convertTime); + + sysTime.wYear = newtime->tm_year + 1900; + sysTime.wMonth = newtime->tm_mon + 1; + sysTime.wDayOfWeek = newtime->tm_wday; + sysTime.wDay = newtime->tm_mday; + sysTime.wHour = newtime->tm_hour; + sysTime.wMinute = newtime->tm_min; + sysTime.wSecond = newtime->tm_sec; + + wchar_t rfcDate[64] = {0}, rfcTime[64] = {0}; + GetDateFormat(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), 0, &sysTime, L"ddd',' d MMM yyyy ", rfcDate, 64); + GetTimeFormat(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), 0, &sysTime, L"HH':'mm':'ss 'GMT'", rfcTime, 64); + StringCchPrintf(data_str,len,L"%s%s",rfcDate,rfcTime); +} + +static int getMonthOfYear(const wchar_t *str); +static int validateTime(struct tm *tmloc, int gmoffset); +static const wchar_t *getNextField(const wchar_t *pos); +static int getGMOffset(const wchar_t *str); + +enum +{ + DAY_OF_MONTH = 0, + MONTH_OF_YEAR, + YEAR, + TIME, + TIMEZONE, + DATE_END +}; + +__time64_t MakeRFCDate(const wchar_t *date) +{ + __time64_t result = 0; + const wchar_t *pos = date; + tm tmloc = {0}; + const wchar_t *strmin; + const wchar_t *strsec; + int gmoffset = 1; + int state = DAY_OF_MONTH; + tzset(); + /* skip weekday if present */ + while (pos && *pos && !iswdigit(*pos)) pos++; + + do + { + switch (state) + { + case DAY_OF_MONTH: + tmloc.tm_mday = _wtoi(pos); + break; + case MONTH_OF_YEAR: + tmloc.tm_mon = getMonthOfYear(pos); + break; + case YEAR: + { + /* TODO: we're only accepting 4-digit dates...*/ + const wchar_t *test = pos; int numDigits = 0; + while (iswdigit(*test) && *test) { test++; numDigits++; } + if (numDigits == 2) // let's hope we never have 2 digit years! + tmloc.tm_year = _wtoi(pos) + 100; // assume 2 digit years are 20xx + else + tmloc.tm_year = _wtoi(pos) - 1900; + } + break; + case TIME: + strmin = wcschr(pos, L':'); + strsec = strmin ? wcschr(strmin + 1, L':') : 0; + + tmloc.tm_hour = _wtoi(pos); + if (!strmin) return _time64(0); + tmloc.tm_min = _wtoi(strmin + 1); + if (!strsec) return _time64(0); + tmloc.tm_sec = _wtoi(strsec + 1); + break; + case TIMEZONE: + gmoffset = getGMOffset(pos); + break; + case DATE_END: + pos = 0; + break; + } + + state++; + } + while ((pos = getNextField(pos))); + + tmloc.tm_isdst = 0; //_daylight; + + if (validateTime(&tmloc, gmoffset)) + { + result = _mktime64(&tmloc) - _timezone; + //if (_daylight) + } + + return result; +} + +const wchar_t *getNextField(const wchar_t *pos) +{ + if (!pos) + return 0; + while (pos && *pos && !iswspace(*pos)) pos++; + while (pos && *pos && iswspace(*pos)) pos++; + + return ((pos && *pos) ? pos : 0); +} + +int validateTime(struct tm *tmloc, int gmoffset) +{ + int result = 1; + + if (tmloc->tm_mday < 1 || tmloc->tm_mday > 31 || + tmloc->tm_mon < 0 || tmloc->tm_mon > 11 || + tmloc->tm_year < 0 || tmloc->tm_year > 2000 || + tmloc->tm_hour < 0 || tmloc->tm_hour > 23 || + tmloc->tm_min < 0 || tmloc->tm_min > 59 || + tmloc->tm_sec < 0 || tmloc->tm_sec > 59 || + gmoffset == 1) + result = 0; + + return result; +} + +int getMonthOfYear(const wchar_t *str) +{ + int mon = -1; + /* This is not the most efficient way to determine + the month (we could use integer comparisons, for instance) + but I don't think this will be a performance bottleneck :) + */ + + if (!wcsnicmp(str, L"Jan", 3)) + mon = 0; + else if (!wcsnicmp(str, L"Feb", 3)) + mon = 1; + else if (!wcsnicmp(str, L"Mar", 3)) + mon = 2; + else if (!wcsnicmp(str, L"Apr", 3)) + mon = 3; + else if (!wcsnicmp(str, L"May", 3)) + mon = 4; + else if (!wcsnicmp(str, L"Jun", 3)) + mon = 5; + else if (!wcsnicmp(str, L"Jul", 3)) + mon = 6; + else if (!wcsnicmp(str, L"Aug", 3)) + mon = 7; + else if (!wcsnicmp(str, L"Sep", 3)) + mon = 8; + else if (!wcsnicmp(str, L"Oct", 3)) + mon = 9; + else if (!wcsnicmp(str, L"Nov", 3)) + mon = 10; + else if (!wcsnicmp(str, L"Dec", 3)) + mon = 11; + + return mon; +} + +int getGMOffset(const wchar_t *str) +{ + int secs = 0; + /* See note in getMonthOfYear() */ + + if (!wcsnicmp(str, L"UT", 2) || !wcsnicmp(str, L"GMT", 3)) + secs = 0; + else if (!wcsnicmp(str, L"EDT", 3)) + secs = -4 * 3600; + else if (!wcsnicmp(str, L"PST", 3)) + secs = -8 * 3600; + else if (!wcsnicmp(str, L"EST", 3) || !wcsnicmp(str, L"CDT", 3)) + secs = -5 * 3600; + else if (!wcsnicmp(str, L"MST", 3) || !wcsnicmp(str, L"PDT", 3)) + secs = -7 * 3600; + else if (!wcsnicmp(str, L"CST", 3) || !wcsnicmp(str, L"MDT", 3)) + secs = -6 * 3600; + else if ( (str[0] == L'+' || str[0] == L'-') && + iswdigit(str[1]) && iswdigit(str[2]) && + iswdigit(str[3]) && iswdigit(str[4])) + { + secs = 3600 * (10 * (str[1] - 48) + str[2] - 48); + secs += 60 * (10 * (str[3] - 48) + str[4] - 48); + + if (str[0] == L'-') + secs = -secs; + } + else + secs = 1; + + return secs; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/RFCDate.h b/Src/Plugins/Library/ml_downloads/RFCDate.h new file mode 100644 index 00000000..7606272f --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/RFCDate.h @@ -0,0 +1,7 @@ +#ifndef NULLSOFT_RFCDATEH +#define NULLSOFT_RFCDATEH + +void MakeRFCDateString(__time64_t convertTime, wchar_t *data_str, size_t len); +__time64_t MakeRFCDate(const wchar_t *date); +void MakeDateString(__time64_t convertTime, wchar_t *date_str, size_t len); +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/TODO.txt b/Src/Plugins/Library/ml_downloads/TODO.txt new file mode 100644 index 00000000..db9d5965 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/TODO.txt @@ -0,0 +1,51 @@ +to fix
+----
+crash on shutdown
+
+
+/////------------ 1.1 below --------------
+automatically switch to 'custom' if you click on dropdown in "add" or "edit" url dialog
+
+need icon for listened media
+maybe one for read text?
+
+strip whitespace from beginning of titles
+
+multiple-select
+
+Allow for customizing the download location in add/edit url
+
+deletable items (needs to move to a separate 'deleted items' list so we don't re-add them next rss refresh)
+
+drag-n-drop from webpages
+
+once we get an HTTP 200, we should put the downloaded on the 'downloads' list, and be able to update the download percentage status as necessary
+
+BACKGROUND DOWNLOADER
+<<<
+avoid multiple downloads of the same thing
+avoid downlaoding things that have already been downloaded.
+range / if-range to handle download resuming
+save the last modified dates from "Last-Modified" header
+save unfinished downloads to an XML file and read on load
+>>>
+
+UNIFIED DOWNLOAD MANAGER CONCEPT !!!!!
+
+who needs updates
+downloaded file list
+downloads page (to refresh view)
+item object
+podcast page (to refresh view)
+
+
+new way of listing items
+---
+create a common "items" data structure that select channels add their items to.
+When a channel is select, it adds its items.
+When a channel is deselected, it removes its items.
+When a channel is refreshed, it re-adds its items (assuming the item-adder function protects against dupes)
+only potential issue is if a channel somehow "loses" items (or an item's GUID is changes)
+could be fixed by either 1) keeps track of "parent channel" in the items list
+2) rebuilding the entire items list on every channel refresh
+or 3) preventing GUID changes and item deletions (or forcing an item list rebuild if it does occur)
diff --git a/Src/Plugins/Library/ml_downloads/Util.h b/Src/Plugins/Library/ml_downloads/Util.h new file mode 100644 index 00000000..12acd266 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/Util.h @@ -0,0 +1,64 @@ +#ifndef NULLSOFT_PODCAST_PLUGIN_UTIL_HEADER +#define NULLSOFT_PODCAST_PLUGIN_UTIL_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../nu/trace.h" + +#define CSTR_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) + +#ifndef ARRAYSIZE +#define ARRAYSIZE(blah) (sizeof(blah)/sizeof(*blah)) +#endif + +#ifndef LONGX86 +#ifdef _WIN64 + #define LONGX86 LONG_PTR +#else /*_WIN64*/ + #define LONGX86 LONG +#endif /*_WIN64*/ +#endif // LONGX86 + +#ifdef __cplusplus + #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) ::SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam)) +#else + #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam)) +#endif // __cplusplus + +#define SENDMLIPC(__hwndML, __ipcMsgId, __param) SENDMSG((__hwndML), WM_ML_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId)) +#define SENDWAIPC(__hwndWA, __ipcMsgId, __param) SENDMSG((__hwndWA), WM_WA_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId)) + +#define MSGRESULT(__hwnd, __result) { SetWindowLongPtrW((__hwnd), DWL_MSGRESULT, ((LONGX86)(LONG_PTR)(__result))); return TRUE; } + +#define SENDCMD(__hwnd, __ctrlId, __eventId, __hctrl) (SENDMSG((__hwnd), WM_COMMAND, MAKEWPARAM(__ctrlId, __eventId), (LPARAM)(__hctrl))) + + +LPWSTR Plugin_MallocString(size_t cchLen); +LPWSTR Plugin_ReAllocString(LPWSTR pszString, size_t cchLen); +void Plugin_FreeString(LPWSTR pszString); +LPWSTR Plugin_CopyString(LPCWSTR pszSource); + +LPSTR Plugin_MallocAnsiString(size_t cchLen); +LPSTR Plugin_CopyAnsiString(LPCSTR pszSource); +void Plugin_FreeAnsiString(LPSTR pszString); + +LPWSTR Plugin_DuplicateResString(LPCWSTR pszResource); +void Plugin_FreeResString(LPWSTR pszResource); +HRESULT Plugin_CopyResString(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszString); + +LPSTR Plugin_WideCharToMultiByte(UINT codePage, DWORD dwFlags, LPCWSTR lpWideCharStr, INT cchWideChar, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar); +LPWSTR Plugin_MultiByteToWideChar(UINT codePage, DWORD dwFlags, LPCSTR lpMultiByteStr, INT cbMultiByte); + +void Plugin_SafeRelease(IUnknown *pUnk); + + +HRESULT Plugin_FileExtensionFromUrl(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszUrl, LPCWSTR defaultExtension); +void Plugin_ReplaceBadPathChars(LPWSTR pszPath); +INT Plugin_CleanDirectory(LPWSTR pszPath); + +HRESULT Plugin_EnsurePathExist(LPCWSTR pszDirectory); + +#endif // NULLSOFT_PODCAST_PLUGIN_UTIL_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/XMLWriter.cpp b/Src/Plugins/Library/ml_downloads/XMLWriter.cpp new file mode 100644 index 00000000..fdc8f2ca --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/XMLWriter.cpp @@ -0,0 +1,125 @@ +#include "Main.h" +#include "RFCDate.h" +#include "Downloaded.h" +#include "../nu/AutoLock.h" +#include "Defaults.h" +#include "../Agave/Language/api_language.h" + +using namespace Nullsoft::Utility; + +static void XMLWriteString(FILE *fp, const wchar_t *str) +{ + for (const wchar_t *itr = str; *itr; itr=CharNextW(itr)) + { + switch (*itr) + { + case '<': fputws(L"<", fp); break; + case '>': fputws(L">", fp); break; + case '&': fputws(L"&", fp); break; + case '\"': fputws(L""", fp); break; + case '\'': fputws(L"'", fp); break; + default: fputwc(*itr, fp); break; + } + } +} + +static void InstaProp(FILE *fp, const wchar_t *property, const wchar_t *value) +{ + fwprintf(fp, L" %s=\"", property); + XMLWriteString(fp, value); + fputws(L"\"", fp); +} + +static void InstaProp(FILE *fp, const wchar_t *property, int value) +{ + fwprintf(fp, L" %s=\"%i\"", property, value); +} + +static void InstaPropI64(FILE *fp, const wchar_t *property, int64_t value) +{ + fwprintf(fp, L" %s=\"%I64d\"", property, value); +} + +static void InstaPropTime(FILE *fp, const wchar_t *property, __time64_t value) +{ + fwprintf(fp, L" %s=\"%I64u\"", property, value); +} + +static void InstaProp(FILE *fp, const wchar_t *property, float value) +{ + _fwprintf_l(fp, L" %s=\"%3.3f\"", WASABI_API_LNG->Get_C_NumericLocale(), property, value); +} + +static void InstaProp(FILE *fp, const wchar_t *property, bool value) +{ + fwprintf(fp, L" %s=\"", property); + if (value) + fputws(L"true\"", fp); + else + fputws(L"false\"", fp); +} + +static void InstaTag(FILE *fp, const wchar_t *tag, const wchar_t *content) +{ + if (content && !((INT_PTR)content < 65536) && *content) + { + fwprintf(fp, L"<%s>", tag); + XMLWriteString(fp, content); + fwprintf(fp, L"</%s>\r\n", tag); + } +} + +static void InstaTag(FILE *fp, const wchar_t *tag, unsigned int uint) +{ + fwprintf(fp, L"<%s>%u</%s>", tag, uint, tag); +} + +static void InstaTag(FILE *fp, const wchar_t *tag, __time64_t uint) +{ + fwprintf(fp, L"<%s>%I64u</%s>", tag, uint, tag); +} + +static void BuildXMLDownloads(FILE *fp, DownloadList &downloads) +{ + fputws(L"<downloads", fp); + InstaProp(fp, L"downloadsTitleWidth", downloadsTitleWidth); + InstaProp(fp, L"downloadsProgressWidth", downloadsProgressWidth); + InstaProp(fp, L"downloadsDateWidth", downloadsDateWidth); + InstaProp(fp, L"downloadsSourceWidth", downloadsSourceWidth); + InstaProp(fp, L"downloadsPathWidth", downloadsPathWidth); + InstaProp(fp, L"downloadsItemSort", downloadsItemSort); + InstaProp(fp, L"downloadsSortAscending", downloadsSortAscending); + fputws(L">\r\n", fp); + + AutoLock lock (downloads); + DownloadList::const_iterator itr; + for ( itr = downloads.begin(); itr != downloads.end(); itr++ ) + { + fputws(L"<download>", fp); + InstaTag(fp, L"source", itr->source); + InstaTag(fp, L"title", itr->title); + InstaTag(fp, L"url", itr->url); + InstaTag(fp, L"path", itr->path); + InstaTag(fp, L"size", (unsigned int)itr->totalSize); + InstaTag(fp, L"downloadDate", itr->downloadDate); + wchar_t status[64] = {0}; + _itow(itr->downloadStatus, status, 10); + InstaTag(fp, L"downloadStatus", status); + fputws(L"</download>\r\n", fp); + } + + fputws(L"</downloads>", fp); +} + +void SaveDownloads(const wchar_t *fileName, DownloadList &downloads) +{ + FILE *fp = _wfopen(fileName, L"wb"); + if (fp) + { + wchar_t BOM = 0xFEFF; + fwrite(&BOM, sizeof(BOM), 1, fp); + fputws(L"<?xml version=\"1.0\" encoding=\"UTF-16\"?>\r\n", fp); + BuildXMLDownloads(fp, downloads); + fclose(fp); + } +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/XMLWriter.h b/Src/Plugins/Library/ml_downloads/XMLWriter.h new file mode 100644 index 00000000..26389756 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/XMLWriter.h @@ -0,0 +1,6 @@ +#ifndef NULLSOFT_XMLWRITERH +#define NULLSOFT_XMLWRITERH +#include "Downloaded.h" + +void SaveDownloads(const wchar_t *fileName, DownloadList &downloads); +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/api__ml_downloads.h b/Src/Plugins/Library/ml_downloads/api__ml_downloads.h new file mode 100644 index 00000000..574b0bf2 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/api__ml_downloads.h @@ -0,0 +1,11 @@ +#ifndef NULLSOFT_ML_DOWNLOADS_API_H +#define NULLSOFT_ML_DOWNLOADS_API_H + +#include "../Agave/Language/api_language.h" + +#include "api/application/api_application.h" +#define WASABI_API_APP applicationApi + +#include "../Agave/ExplorerFindFile/api_explorerfindfile.h" + +#endif // !NULLSOFT_ML_DOWNLOADS_API_H
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/date.c b/Src/Plugins/Library/ml_downloads/date.c new file mode 100644 index 00000000..bbe876c6 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/date.c @@ -0,0 +1 @@ +#include "time.h" diff --git a/Src/Plugins/Library/ml_downloads/date.h b/Src/Plugins/Library/ml_downloads/date.h new file mode 100644 index 00000000..58204571 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/date.h @@ -0,0 +1,20 @@ +#ifndef _OD_DATE_ +#define _OD_DATE_ + +#include <time.h> + + + +/* These functions all work with "Internet"(RFC 822) format Date/Time strings only */ +/* An example of an RFC 822 format Date/Time string is "Thu, 28 Aug 2003 21:30:47 EDT" */ + +/* converts the RFC 822 format date string into UTC Calendar time */ +time_t getDateSecs(char *date); + +/* returns a string that represents the given UTC Calendar time value as an + RFC 822 format string. The buffer is user-supplied and must be at least + 30 bytes in size. */ +char *getDateStr(time_t tmval, char *buffer, int gmt); + +#endif /*_OD_DATE_*/ + diff --git a/Src/Plugins/Library/ml_downloads/db.cpp b/Src/Plugins/Library/ml_downloads/db.cpp new file mode 100644 index 00000000..db0e1b5b --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/db.cpp @@ -0,0 +1,150 @@ +#include <shlwapi.h> + +#include "api__ml_downloads.h" +#include "Downloaded.h" +#include "../nde/nde_c.h" +#include "../nu/AutoLock.h" + +/* DB Schema +Source +Url +Title +DownloadDate +Length +Filename +*/ + +static Nullsoft::Utility::LockGuard dbcs; +static nde_table_t table = 0; +static nde_database_t db = 0; +using namespace Nullsoft::Utility; + +enum +{ + DB_ID_SOURCE = 0, + DB_ID_URL = 1, + DB_ID_TITLE = 2, + DB_ID_DOWNLOADDATE = 3, + DB_ID_LENGTH = 4, + DB_ID_FILENAME = 5 +}; + +static bool OpenDatabase() +{ + AutoLock lock(dbcs); + if (!db) + db = NDE_CreateDatabase(); + + return true; +} + +void CloseDatabase() +{ + AutoLock lock( dbcs ); + if ( db ) + { + if ( table ) + NDE_Database_CloseTable( db, table ); + + NDE_DestroyDatabase( db ); + } + + db = 0; +} + +static void CreateFields( nde_table_t table ) +{ + // create defaults + NDE_Table_NewColumnW( table, DB_ID_SOURCE, L"source", FIELD_STRING ); + NDE_Table_NewColumnW( table, DB_ID_URL, L"url", FIELD_STRING ); + NDE_Table_NewColumnW( table, DB_ID_TITLE, L"title", FIELD_STRING ); + NDE_Table_NewColumnW( table, DB_ID_DOWNLOADDATE, L"downloaddate", FIELD_DATETIME ); + NDE_Table_NewColumnW( table, DB_ID_LENGTH, L"length", FIELD_INTEGER ); + NDE_Table_NewColumnW( table, DB_ID_FILENAME, L"filename", FIELD_FILENAME ); + NDE_Table_PostColumns( table ); + NDE_Table_AddIndexByIDW( table, DB_ID_URL, L"url" ); +} + +static bool OpenTable() +{ + AutoLock lock( dbcs ); + if ( !OpenDatabase() ) + return false; + + if ( !table ) + { + const wchar_t *inidir = WASABI_API_APP->path_getUserSettingsPath(); + wchar_t tablePath[ MAX_PATH ] = { 0 }, indexPath[ MAX_PATH ] = { 0 }; + + PathCombineW( tablePath, inidir, L"plugins" ); + PathAppendW( tablePath, L"downloads.dat" ); + + PathCombineW( indexPath, inidir, L"plugins" ); + PathAppendW( indexPath, L"downloads.idx" ); + + table = NDE_Database_OpenTable( db, tablePath, indexPath, NDE_OPEN_ALWAYS, NDE_CACHE ); + if ( table ) + CreateFields( table ); + } + + return !!table; +} + +static void db_add( nde_scanner_t s, unsigned char id, wchar_t *data ) +{ + if ( data ) + { + nde_field_t f = NDE_Scanner_NewFieldByID( s, id ); + NDE_StringField_SetString( f, data ); + } +} + +static void db_add_int( nde_scanner_t s, unsigned char id, int data ) +{ + if ( data ) + { + nde_field_t f = NDE_Scanner_NewFieldByID( s, id ); + NDE_IntegerField_SetValue( f, data ); + } +} + +static void db_add_time( nde_scanner_t s, unsigned char id, time_t data ) +{ + if ( data ) + { + nde_field_t f = NDE_Scanner_NewFieldByID( s, id ); + NDE_IntegerField_SetValue( f, static_cast<int>( data ) ); + } +} + +bool AddDownloadData( const DownloadedFile &data ) +{ + AutoLock lock( dbcs ); + if ( !OpenTable() ) + return false; + + nde_scanner_t s = NDE_Table_CreateScanner( table ); + if ( s ) + { + NDE_Scanner_New( s ); + db_add( s, DB_ID_SOURCE, data.source ); + db_add( s, DB_ID_URL, data.url ); + db_add( s, DB_ID_TITLE, data.title ); + db_add_time( s, DB_ID_DOWNLOADDATE, data.downloadDate ); + db_add_int( s, DB_ID_LENGTH, (int)data.totalSize ); + db_add( s, DB_ID_FILENAME, data.path ); + NDE_Scanner_Post( s ); + NDE_Table_DestroyScanner( table, s ); + NDE_Table_Sync( table ); + + return true; + } + return false; +} + +void CompactDatabase() +{ + AutoLock lock( dbcs ); + if ( OpenTable() ) + NDE_Table_Compact( table ); +} diff --git a/Src/Plugins/Library/ml_downloads/errors.h b/Src/Plugins/Library/ml_downloads/errors.h new file mode 100644 index 00000000..29766060 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/errors.h @@ -0,0 +1,15 @@ +#ifndef NULLSOFT_ML_DOWNLOADS_ERRORS_H +#define NULLSOFT_ML_DOWNLOADS_ERRORS_H + +enum +{ + DOWNLOAD_SUCCESS = 0, + DOWNLOAD_404, + DOWNLOAD_TIMEOUT, + DOWNLOAD_NOHTTP, + DOWNLOAD_NOPARSER, + DOWNLOAD_CONNECTIONRESET, + DOWNLOAD_ERROR_PARSING_XML, +}; + +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/layout.cpp b/Src/Plugins/Library/ml_downloads/layout.cpp new file mode 100644 index 00000000..bfdd9591 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/layout.cpp @@ -0,0 +1,215 @@ +#include "./layout.h" + +#include <windows.h> + +BOOL Layout_Initialize( HWND hwnd, const INT *itemList, INT itemCount, LAYOUTITEM *layout ) +{ + if ( NULL == itemList || NULL == layout ) + return FALSE; + + LAYOUTITEM *item; + + for ( INT i = 0; i < itemCount; i++ ) + { + item = &layout[ i ]; + item->hwnd = GetDlgItem( hwnd, itemList[ i ] ); + if ( item->hwnd == NULL ) + continue; + + if ( FALSE == GetWindowRect( item->hwnd, &item->rect ) ) + SetRectEmpty( &item->rect ); + else + MapWindowPoints( HWND_DESKTOP, hwnd, (POINT *)&item->rect, 2 ); + + item->cx = item->rect.right - item->rect.left; + item->cy = item->rect.bottom - item->rect.top; + item->x = item->rect.left; + item->y = item->rect.top; + item->flags = 0; + } + + return TRUE; +} + +BOOL Layout_Perform( HWND hwnd, LAYOUTITEM *layout, INT layoutCount, BOOL fRedraw ) +{ + HDWP hdwp, hdwpTemp; + hdwp = BeginDeferWindowPos( layoutCount ); + if ( hdwp == NULL ) + return FALSE; + + UINT baseFlags = SWP_NOACTIVATE | SWP_NOZORDER; + if ( fRedraw == FALSE ) + baseFlags |= ( SWP_NOREDRAW | SWP_NOCOPYBITS ); + + LAYOUTITEM *item; + for ( INT i = 0; i < layoutCount; i++ ) + { + item = &layout[ i ]; + if ( item->hwnd == NULL ) + continue; + + UINT flags = baseFlags | ( item->flags & ~( SWP_HIDEWINDOW | SWP_SHOWWINDOW ) ); + if ( item->x == item->rect.left && item->y == item->rect.top ) + flags |= SWP_NOMOVE; + + if ( item->cx == ( item->rect.right - item->rect.left ) && item->cy == ( item->rect.bottom - item->rect.top ) ) + flags |= SWP_NOSIZE; + + if ( ( SWP_HIDEWINDOW & item->flags ) != 0 ) + { + UINT windowStyle = (UINT)GetWindowLongPtr( item->hwnd, GWL_STYLE ); + if ( ( WS_VISIBLE & windowStyle ) != 0 ) + { + SetWindowLongPtr( item->hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE ); + if ( FALSE != fRedraw ) + { + RedrawWindow( hwnd, &item->rect, NULL, RDW_INVALIDATE | RDW_ERASE ); + } + } + } + + if ( ( SWP_NOSIZE | SWP_NOMOVE ) != ( ( SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED ) & flags ) ) + { + hdwpTemp = DeferWindowPos( hdwp, item->hwnd, NULL, item->x, item->y, item->cx, item->cy, flags ); + if ( hdwpTemp == NULL ) + break; + + hdwp = hdwpTemp; + } + } + + BOOL result = ( hdwp != NULL ) ? EndDeferWindowPos( hdwp ) : FALSE; + + for ( INT i = 0; i < layoutCount; i++ ) + { + item = &layout[ i ]; + if ( NULL != item->hwnd && 0 != ( SWP_SHOWWINDOW & item->flags ) ) + { + UINT windowStyle = (UINT)GetWindowLongPtr( item->hwnd, GWL_STYLE ); + if ( 0 == ( WS_VISIBLE & windowStyle ) ) + { + SetWindowLongPtr( item->hwnd, GWL_STYLE, windowStyle | WS_VISIBLE ); + if ( FALSE != fRedraw ) + RedrawWindow( item->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN ); + } + } + } + + return result; +} + +static void Layout_SetItemVisibility( const RECT *rect, LAYOUTITEM *item ) +{ + if ( NULL == item || NULL == item->hwnd ) + return; + + BOOL outsider = ( item->cx <= 0 || item->cy <= 0 || + item->x >= rect->right || item->y >= rect->bottom || + ( item->x + item->cx ) < rect->left || ( item->y + item->cy ) < rect->top ); + + UINT windowStyle = (UINT)GetWindowLongPtr( item->hwnd, GWL_STYLE ); + if ( 0 == ( WS_VISIBLE & windowStyle ) ) + { + if ( !outsider ) + { + item->flags |= SWP_SHOWWINDOW; + } + } + else + { + if ( outsider ) + { + item->flags |= SWP_HIDEWINDOW; + } + } +} + +BOOL Layout_SetVisibility( const RECT *rect, LAYOUTITEM *layout, INT layoutCount ) +{ + if ( NULL == rect || NULL == layout ) + return FALSE; + + for ( INT i = 0; i < layoutCount; i++ ) + { + Layout_SetItemVisibility( rect, &layout[ i ] ); + } + + return TRUE; +} + +BOOL Layout_SetVisibilityEx( const RECT *rect, const INT *indexList, INT indexCount, LAYOUTITEM *layout ) +{ + if ( NULL == rect || NULL == indexList || NULL == layout ) + return FALSE; + + for ( INT i = 0; i < indexCount; i++ ) + { + Layout_SetItemVisibility( rect, &layout[ indexList[ i ] ] ); + } + + return TRUE; +} + +BOOL Layout_GetValidRgn( HRGN validRgn, POINTS parrentOffset, const RECT *validRect, LAYOUTITEM *layout, INT layoutCount ) +{ + if ( NULL == validRgn ) + return FALSE; + + SetRectRgn( validRgn, 0, 0, 0, 0 ); + + if ( NULL == layout ) + return FALSE; + + HRGN rgn = CreateRectRgn( 0, 0, 0, 0 ); + if ( NULL == rgn ) + return FALSE; + + LAYOUTITEM *item; + LONG l, t, r, b; + + for ( INT i = 0; i < layoutCount; i++ ) + { + item = &layout[ i ]; + if ( NULL != item->hwnd && 0 == ( ( SWP_HIDEWINDOW | SWP_SHOWWINDOW ) & item->flags ) ) + { + l = item->x + parrentOffset.x; + t = item->y + parrentOffset.y; + r = l + item->cx; + b = t + item->cy; + if ( 0 != ( SWP_NOREDRAW & item->flags ) || ( l == item->rect.left && t == item->rect.top && r == item->rect.right && b == item->rect.bottom ) ) + { + if ( NULL != validRect ) + { + if ( l < validRect->left ) + l = validRect->left; + + if ( t < validRect->top ) + t = validRect->top; + + if ( r > validRect->right ) + r = validRect->right; + + if ( b > validRect->bottom ) + b = validRect->bottom; + } + + if ( l < r && t < b ) + { + SetRectRgn( rgn, l, t, r, b ); + CombineRgn( validRgn, validRgn, rgn, RGN_OR ); + + if ( NULLREGION != GetUpdateRgn( item->hwnd, rgn, FALSE ) ) + { + OffsetRgn( rgn, parrentOffset.x, parrentOffset.y ); + CombineRgn( validRgn, validRgn, rgn, RGN_DIFF ); + } + } + } + } + } + + DeleteObject( rgn ); + + return TRUE; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/layout.h b/Src/Plugins/Library/ml_downloads/layout.h new file mode 100644 index 00000000..9af73396 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/layout.h @@ -0,0 +1,43 @@ +#ifndef NULLSOFT_PODCAST_PLUGIN_LAYOUT_HEADER +#define NULLSOFT_PODCAST_PLUGIN_LAYOUT_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +typedef struct __LAYOUTITEM +{ + HWND hwnd; + LONG x; + LONG y; + LONG cx; + LONG cy; + UINT flags; + RECT rect; +} LAYOUTITEM; + + +#define LI_GET_R(__li) ((__li).x + (__li).cx) +#define LI_GET_B(__li) ((__li).y + (__li).cy) +#define LI_EXPAND_W(__li, __delta) { (__li).cx += (__delta); } +#define LI_EXPAND_H(__li, __delta) { (__li).cy += (__delta); } +#define LI_SHIFT_L(__li, __delta) { (__li).x += (__delta); } +#define LI_SHIFT_T(__li, __delta) { { (__li).y += (__delta); } +#define LI_SET_L(__li, __val) { (__li).x = (__val); } +#define LI_SET_T(__li, __val) { (__li).y = (__val); } +#define LI_SET_W(__li, __val) { (__li).cx = (__val); } +#define LI_SET_H(__li, __val) { (__li).cy = (__val); } +#define LI_SET_R(__li, __val) { (__li).cx = ((__val) - (__li).x); } +#define LI_SET_B(__li, __val) { (__li).cy = ((__val) - (__li).y); } + +BOOL Layout_Initialize(HWND hwnd, const INT *itemList, INT itemCount, LAYOUTITEM *layout); +BOOL Layout_SetVisibilityEx(const RECT *rect, const INT *indexList, INT indexCount, LAYOUTITEM *layout); +BOOL Layout_SetVisibility(const RECT *rect, LAYOUTITEM *layout, INT layoutCount); +BOOL Layout_Perform(HWND hwnd, LAYOUTITEM *layout, INT layoutCount, BOOL fRedraw); + +BOOL Layout_GetValidRgn(HRGN validRgn, POINTS parrentOffset, const RECT *validRect, LAYOUTITEM *layout, INT layoutCount); + + +#endif //NULLSOFT_PODCAST_PLUGIN_LAYOUT_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/ml_downloads.rc b/Src/Plugins/Library/ml_downloads/ml_downloads.rc new file mode 100644 index 00000000..35737775 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/ml_downloads.rc @@ -0,0 +1,249 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_DOWNLOADS DIALOGEX 0, 0, 266, 92 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_DOWNLOADLIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | LVS_OWNERDATA | WS_TABSTOP,0,0,264,79 + CONTROL "Play",IDC_PLAY,"Button",BS_OWNERDRAW | WS_TABSTOP,0,81,37,11 + CONTROL "Enqueue",IDC_ENQUEUE,"Button",BS_OWNERDRAW | WS_TABSTOP,39,81,37,11 + CONTROL "Remove",IDC_REMOVE,"Button",BS_OWNERDRAW | WS_TABSTOP,80,81,37,11 + CONTROL "Clean up",IDC_CLEANUP,"Button",BS_OWNERDRAW | WS_TABSTOP,120,81,37,11 + CONTROL "",IDC_STATUS,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | SS_ENDELLIPSIS | WS_GROUP,163,81,102,11 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MENU1 MENU +BEGIN + POPUP "Downloads" + BEGIN + MENUITEM "Play selection\tEnter", IDC_PLAY + MENUITEM "Enqueue selection\tShift+Enter", IDC_ENQUEUE + POPUP "Send to:" + BEGIN + MENUITEM "", ID_Menu + END + MENUITEM SEPARATOR + MENUITEM "Select all\tCtrl+A", IDC_SELECTALL + MENUITEM "View file info...\tAlt+3", IDC_INFOBOX + MENUITEM "Explore item folder\tCtrl+F", ID_DOWNLOADS_EXPLORERITEMFOLDER + MENUITEM SEPARATOR + MENUITEM "Remove from list\tDel", IDC_REMOVE + MENUITEM "Delete file\tShift+Del", IDC_DELETE + MENUITEM "Cancel download", ID_DOWNLOADS_CANCELDOWNLOAD + END + POPUP "Navigation" + BEGIN + MENUITEM "&Preferences", ID_NAVIGATION_PREFERENCES + MENUITEM SEPARATOR + MENUITEM "Help", ID_NAVIGATION_HELP + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_TREEITEM_DOWNLOADS BITMAP "resources\\downloadIcon.bmp" +IDB_TREEITEM_DOWNLOADS1 BITMAP "resources\\downloadIcon1.bmp" +IDB_TREEITEM_DOWNLOADS2 BITMAP "resources\\downloadIcon2.bmp" +IDB_TREEITEM_DOWNLOADS3 BITMAP "resources\\downloadIcon3.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_VIEW_DOWNLOAD_ACCELERATORS ACCELERATORS +BEGIN + "F", ID_DOWNLOADS_EXPLORERITEMFOLDER, VIRTKEY, CONTROL, NOINVERT + VK_DELETE, IDC_DELETE, VIRTKEY, SHIFT, NOINVERT + VK_RETURN, IDC_ENQUEUE, VIRTKEY, SHIFT, NOINVERT + "3", IDC_INFOBOX, VIRTKEY, ALT, NOINVERT + VK_RETURN, IDC_PLAY, VIRTKEY, NOINVERT + VK_DELETE, IDC_REMOVE, VIRTKEY, NOINVERT + "A", IDC_SELECTALL, VIRTKEY, CONTROL, NOINVERT + VK_RETURN, IDC_CUSTOM, VIRTKEY, SHIFT, CONTROL, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_PLUGIN_NAME "Nullsoft Downloads v%d.%02d" + 65535 "{706549D3-D813-45dd-9A0B-E3793A1B63A8}" +END + +STRINGTABLE +BEGIN + IDS_DOWNLOADS "Downloads" + IDS_DOWNLOADING "Downloading..." + IDS_CHOOSE_FOLDER "Choose folder to store downloaded media." + IDS_FILE_NOT_FOUND "Cannot connect to %s\nFile not Found." + IDS_CONNECTION_TIMED_OUT "Cannot connect to %s\nConnection timed out." + IDS_NO_JNETLIB "HTTP Downloader library is missing.\nPlease reinstall Winamp." + IDS_JNETLIB_MISSING "JNetLib missing" + IDS_NO_EXPAT "XML Parsing library is missing.\nPlease reinstall Winamp." +END + +STRINGTABLE +BEGIN + IDS_EXPAT_MISSING "Expat missing" + IDS_WDAY_SUN "Sun" + IDS_WDAY_MON "Mon" + IDS_WDAY_TUE "Tue" + IDS_WDAY_WED "Wed" +END + +STRINGTABLE +BEGIN + IDS_WDAY_THU "Thu" + IDS_WDAY_FRI "Fri" + IDS_WDAY_SAT "Sat" + IDS_MONTH_JAN "Jan" + IDS_MONTH_FEB "Feb" + IDS_MONTH_MAR "Mar" + IDS_MONTH_APR "Apr" + IDS_MONTH_MAY "May" + IDS_MONTH_JUN "Jun" + IDS_MONTH_JUL "Jul" + IDS_MONTH_AUG "Aug" + IDS_MONTH_SEP "Sep" + IDS_MONTH_OCT "Oct" + IDS_MONTH_NOV "Nov" + IDS_MONTH_DEC "Dec" +END + +STRINGTABLE +BEGIN + IDS_INVALID_LINK "Invalid link (404 or timeout)" + IDS_CONNECTION_RESET_BY_PEER "Connection reset by peer." + IDS_DOWNLOADING_PERCENT "Downloading %d%%" + IDS_SOURCE "Source" + IDS_TITLE "Title" + IDS_PROGRESS "Status" + IDS_PATH "Download Location" + IDS_PERM_DELETE_ARE_YOU_SURE + "This will permanently delete this file, are you sure?" + IDS_PERM_DELETE_THESE_ARE_YOU_SURE + "This will permanently delete these %d files, are you sure?" + IDS_DELETION "Deletion" +END + +STRINGTABLE +BEGIN + IDS_CLEAR_ALL_FINISHED_DOWNLOADS + "This will clear all finished downloads, are you sure?\n\nTip: You can view all your downloaded podcasts in Local Media/Podcasts" + IDS_CLEAN_UP_LIST "Clean Up List" + IDS_DONE "Done" + IDS_SAVE "Save" + IDS_REQUIRES_INTERNET_CONNECTION_ENSURE_CONNECTION + "The media library feature you are attempting to use requires an internet connection. Please make sure you are connected to the internet and try again." + IDS_ADD_TO_DOWNLOADS "Added to downloads." + IDS_SURE_YOU_WANT_TO_REMOVE_THIS + "Are you sure you want to remove %s\n(%s)" + IDS_CONFIRM "Confirm" + IDS_CANCEL_DOWNLOADS_AND_QUIT + "There are downloads currently in progress.\nAre you sure you want to cancel these downloads and quit?" + IDS_CONFIRM_QUIT "Confirm Quit" +END + +STRINGTABLE +BEGIN + IDS_DELETEFAILED "Delete Failed" + IDS_SORRY "Sorry" + IDS_DATE "Download Date" + IDS_N_A "n/a" + IDS_SIZE "Size" +END + +STRINGTABLE +BEGIN + IDS_DOWNLOAD_SUCCESS "Completed" + IDS_DOWNLOAD_FAILURE "Failed" + IDS_DOWNLOAD_CANCELED "Cancelled" + IDS_DOWNLOAD_PENDING "Pending..." + IDS_DOWNLOADING_PROGRESS + "Downloading %u file(s), %s of %s complete (%d%%)" + IDS_DOWNLOADING_COMPLETE "Downloading %u files, %s complete" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Library/ml_downloads/ml_downloads.sln b/Src/Plugins/Library/ml_downloads/ml_downloads.sln new file mode 100644 index 00000000..dd12dca8 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/ml_downloads.sln @@ -0,0 +1,93 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29509.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ml_downloads", "ml_downloads.vcxproj", "{23E1FE5D-8833-4095-97D5-A5972CFA477C}" + ProjectSection(ProjectDependencies) = postProject + {57C90706-B25D-4ACA-9B33-95CDB2427C27} = {57C90706-B25D-4ACA-9B33-95CDB2427C27} + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} = {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} + {E105A0A2-7391-47C5-86AC-718003524C3D} = {E105A0A2-7391-47C5-86AC-718003524C3D} + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nde", "..\nde\nde.vcxproj", "{4D25C321-7F8B-424E-9899-D80A364BAF1A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nx", "..\replicant\nx\nx.vcxproj", "{57C90706-B25D-4ACA-9B33-95CDB2427C27}" + ProjectSection(ProjectDependencies) = postProject + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "..\replicant\nu\nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jnetlib", "..\replicant\jnetlib\jnetlib.vcxproj", "{E105A0A2-7391-47C5-86AC-718003524C3D}" + ProjectSection(ProjectDependencies) = postProject + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\replicant\zlib\zlib.vcxproj", "{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {23E1FE5D-8833-4095-97D5-A5972CFA477C}.Debug|Win32.ActiveCfg = Debug|Win32 + {23E1FE5D-8833-4095-97D5-A5972CFA477C}.Debug|Win32.Build.0 = Debug|Win32 + {23E1FE5D-8833-4095-97D5-A5972CFA477C}.Debug|x64.ActiveCfg = Debug|x64 + {23E1FE5D-8833-4095-97D5-A5972CFA477C}.Debug|x64.Build.0 = Debug|x64 + {23E1FE5D-8833-4095-97D5-A5972CFA477C}.Release|Win32.ActiveCfg = Release|Win32 + {23E1FE5D-8833-4095-97D5-A5972CFA477C}.Release|Win32.Build.0 = Release|Win32 + {23E1FE5D-8833-4095-97D5-A5972CFA477C}.Release|x64.ActiveCfg = Release|x64 + {23E1FE5D-8833-4095-97D5-A5972CFA477C}.Release|x64.Build.0 = Release|x64 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Debug|Win32.ActiveCfg = Debug|Win32 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Debug|Win32.Build.0 = Debug|Win32 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Debug|x64.ActiveCfg = Debug|x64 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Debug|x64.Build.0 = Debug|x64 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Release|Win32.ActiveCfg = Release|Win32 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Release|Win32.Build.0 = Release|Win32 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Release|x64.ActiveCfg = Release|x64 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Release|x64.Build.0 = Release|x64 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.ActiveCfg = Debug|Win32 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.Build.0 = Debug|Win32 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.ActiveCfg = Debug|x64 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.Build.0 = Debug|x64 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.ActiveCfg = Release|Win32 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.Build.0 = Release|Win32 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.ActiveCfg = Release|x64 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.Build.0 = Release|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.Build.0 = Debug|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.ActiveCfg = Debug|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.Build.0 = Debug|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.ActiveCfg = Debug|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.Build.0 = Debug|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.ActiveCfg = Release|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.Build.0 = Release|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.ActiveCfg = Release|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.Build.0 = Release|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.ActiveCfg = Debug|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.Build.0 = Debug|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.ActiveCfg = Debug|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.Build.0 = Debug|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.ActiveCfg = Release|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.Build.0 = Release|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.ActiveCfg = Release|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C4E799C1-027B-487B-8E1A-31F2D26A1AFE} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/Library/ml_downloads/ml_downloads.vcxproj b/Src/Plugins/Library/ml_downloads/ml_downloads.vcxproj new file mode 100644 index 00000000..3d27d8bf --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/ml_downloads.vcxproj @@ -0,0 +1,370 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{23E1FE5D-8833-4095-97D5-A5972CFA477C}</ProjectGuid> + <RootNamespace>ml_downloads</RootNamespace> + <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg"> + <VcpkgEnableManifest>false</VcpkgEnableManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>.;..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType> + <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>.;..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType> + <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>.;..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;IGNORE_API_GRACENOTE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>true</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>.;..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;IGNORE_API_GRACENOTE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemGroup> + <ProjectReference Include="..\..\..\nde\nde.vcxproj"> + <Project>{4d25c321-7f8b-424e-9899-d80a364baf1a}</Project> + <CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies> + <ReferenceOutputAssembly>true</ReferenceOutputAssembly> + </ProjectReference> + <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj"> + <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\General\gen_ml\menu.h" /> + <ClInclude Include="..\..\..\nu\Alias.h" /> + <ClInclude Include="..\..\..\nu\ChildSizer.h" /> + <ClInclude Include="..\..\..\nu\DialogSkinner.h" /> + <ClInclude Include="..\..\..\nu\MediaLibraryInterface.h" /> + <ClInclude Include="..\..\..\nu\menushortcuts.h" /> + <ClInclude Include="..\..\..\xml\XMLDOM.h" /> + <ClInclude Include="..\..\..\xml\XMLNode.h" /> + <ClInclude Include="api__ml_downloads.h" /> + <ClInclude Include="Defaults.h" /> + <ClInclude Include="Downloaded.h" /> + <ClInclude Include="DownloadsDialog.h" /> + <ClInclude Include="DownloadsParse.h" /> + <ClInclude Include="DownloadStatus.h" /> + <ClInclude Include="DownloadThread.h" /> + <ClInclude Include="DownloadViewCallback.h" /> + <ClInclude Include="errors.h" /> + <ClInclude Include="layout.h" /> + <ClInclude Include="Main.h" /> + <ClInclude Include="ParseUtil.h" /> + <ClInclude Include="Preferences.h" /> + <ClInclude Include="resource.h" /> + <ClInclude Include="RFCDate.h" /> + <ClInclude Include="Util.h" /> + <ClInclude Include="XMLWriter.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\General\gen_ml\menu.cpp" /> + <ClCompile Include="..\..\General\gen_ml\ml_lib.cpp" /> + <ClCompile Include="..\..\..\nu\ChildSizer.cpp" /> + <ClCompile Include="..\..\..\nu\DialogSkinner.cpp" /> + <ClCompile Include="..\..\..\nu\listview.cpp"> + <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)%(Filename)1.obj</ObjectFileName> + <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(IntDir)%(Filename)1.obj</ObjectFileName> + <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)%(Filename)1.obj</ObjectFileName> + <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)%(Filename)1.obj</ObjectFileName> + </ClCompile> + <ClCompile Include="..\..\..\nu\MediaLibraryInterface.cpp" /> + <ClCompile Include="..\..\..\nu\menushortcuts.cpp" /> + <ClCompile Include="..\..\..\xml\XMLDOM.cpp" /> + <ClCompile Include="..\..\..\xml\XMLNode.cpp" /> + <ClCompile Include="db.cpp" /> + <ClCompile Include="Defaults.cpp" /> + <ClCompile Include="Downloaded.cpp" /> + <ClCompile Include="DownloadsDialog.cpp" /> + <ClCompile Include="DownloadsParse.cpp" /> + <ClCompile Include="DownloadStatus.cpp" /> + <ClCompile Include="DownloadThread.cpp" /> + <ClCompile Include="DownloadViewCallback.cpp" /> + <ClCompile Include="layout.cpp" /> + <ClCompile Include="Main.cpp" /> + <ClCompile Include="ParseUtil.cpp" /> + <ClCompile Include="Preferences.cpp" /> + <ClCompile Include="RFCDate.cpp" /> + <ClCompile Include="util.cpp"> + <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)%(Filename)2.obj</ObjectFileName> + <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(IntDir)%(Filename)2.obj</ObjectFileName> + <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)%(Filename)2.obj</ObjectFileName> + <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)%(Filename)2.obj</ObjectFileName> + </ClCompile> + <ClCompile Include="XMLWriter.cpp" /> + </ItemGroup> + <ItemGroup> + <Image Include="resources\bitmap1.bmp" /> + <Image Include="resources\downloadIcon.bmp" /> + <Image Include="resources\downloadIcon1.bmp" /> + <Image Include="resources\downloadIcon2.bmp" /> + <Image Include="resources\downloadIcon3.bmp" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="ml_downloads.rc" /> + </ItemGroup> + <ItemGroup> + <Text Include="DESIGN.txt" /> + <Text Include="TODO.txt" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/ml_downloads.vcxproj.filters b/Src/Plugins/Library/ml_downloads/ml_downloads.vcxproj.filters new file mode 100644 index 00000000..9883c853 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/ml_downloads.vcxproj.filters @@ -0,0 +1,215 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="db.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Defaults.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Downloaded.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="DownloadsDialog.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="DownloadsParse.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="DownloadStatus.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="DownloadThread.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="DownloadViewCallback.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="layout.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ParseUtil.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Preferences.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="RFCDate.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="util.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="XMLWriter.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nu\ChildSizer.cpp"> + <Filter>Source Files\nu</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nu\DialogSkinner.cpp"> + <Filter>Source Files\nu</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nu\listview.cpp"> + <Filter>Source Files\nu</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nu\MediaLibraryInterface.cpp"> + <Filter>Source Files\nu</Filter> + </ClCompile> + <ClCompile Include="..\..\General\gen_ml\menu.cpp"> + <Filter>Source Files\gen_ml</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nu\menushortcuts.cpp"> + <Filter>Source Files\nu</Filter> + </ClCompile> + <ClCompile Include="..\..\General\gen_ml\ml_lib.cpp"> + <Filter>Source Files\gen_ml</Filter> + </ClCompile> + <ClCompile Include="..\..\..\xml\XMLDOM.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\xml\XMLNode.cpp"> + <Filter>Source Files\xml</Filter> + </ClCompile> + <ClCompile Include="..\..\..\xml\XMLDOM.cpp"> + <Filter>Source Files\xml</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="api__ml_downloads.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Defaults.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Downloaded.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="DownloadsDialog.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="DownloadsParse.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="DownloadStatus.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="DownloadThread.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="DownloadViewCallback.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="errors.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="layout.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Main.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="XMLWriter.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Util.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="RFCDate.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Preferences.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="ParseUtil.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\..\nu\Alias.h"> + <Filter>Header Files\nu</Filter> + </ClInclude> + <ClInclude Include="..\..\..\nu\ChildSizer.h"> + <Filter>Header Files\nu</Filter> + </ClInclude> + <ClInclude Include="..\..\..\nu\DialogSkinner.h"> + <Filter>Header Files\nu</Filter> + </ClInclude> + <ClInclude Include="..\..\..\nu\MediaLibraryInterface.h"> + <Filter>Header Files\nu</Filter> + </ClInclude> + <ClInclude Include="..\..\General\gen_ml\menu.h"> + <Filter>Header Files\gen_ml</Filter> + </ClInclude> + <ClInclude Include="..\..\..\nu\menushortcuts.h"> + <Filter>Header Files\nu</Filter> + </ClInclude> + <ClInclude Include="..\..\..\xml\XMLDOM.h"> + <Filter>Header Files\xml</Filter> + </ClInclude> + <ClInclude Include="..\..\..\xml\XMLNode.h"> + <Filter>Header Files\xml</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Image Include="resources\downloadIcon.bmp"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\downloadIcon1.bmp"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\downloadIcon2.bmp"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\downloadIcon3.bmp"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\bitmap1.bmp"> + <Filter>Image Files</Filter> + </Image> + </ItemGroup> + <ItemGroup> + <Text Include="DESIGN.txt" /> + <Text Include="TODO.txt" /> + </ItemGroup> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{fbb3be6a-b7ea-4313-b14c-3e90ed67b4ec}</UniqueIdentifier> + </Filter> + <Filter Include="Ressource Files"> + <UniqueIdentifier>{52a7174b-e9ac-428f-8f7c-921bcb4e1637}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{dcbaa963-b32c-488a-a8d0-cd04a987d873}</UniqueIdentifier> + </Filter> + <Filter Include="Image Files"> + <UniqueIdentifier>{3d2c389d-dacb-4c28-985e-6404ec435898}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\nu"> + <UniqueIdentifier>{87d018de-72e8-4fe5-94e6-ca03aeb0ede9}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\gen_ml"> + <UniqueIdentifier>{4ec61852-ecfb-400c-ae96-803f47338909}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\xml"> + <UniqueIdentifier>{87422940-a801-4fae-9a04-bac04b096146}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\nu"> + <UniqueIdentifier>{7d1c9f6a-447c-4b7e-bb0a-bd7aef6f1919}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\gen_ml"> + <UniqueIdentifier>{b5409075-f53b-4665-991a-3996953e0b03}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\xml"> + <UniqueIdentifier>{c8ff3a21-0fca-40ea-bce7-8b7492a07ad0}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="ml_downloads.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/png.rc b/Src/Plugins/Library/ml_downloads/png.rc new file mode 100644 index 00000000..5f5d3de1 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/png.rc @@ -0,0 +1,63 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// RCDATA +// + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.\0" +END + + +3 TEXTINCLUDE +BEGIN + "\r\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Library/ml_downloads/resource.h b/Src/Plugins/Library/ml_downloads/resource.h new file mode 100644 index 00000000..c3c33609 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/resource.h @@ -0,0 +1,120 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ml_downloads.rc +// +#define IDS_NULLSOFT_PODCAST 0 +#define IDS_PODCASTS 1 +#define IDS_DOWNLOADING_KB_COMPLETE 2 +#define IDS_DOWNLOADING_KB_PROGRESS 3 +#define IDS_DOWNLOADS 5 +#define IDS_DOWNLOADING 6 +#define IDS_CHOOSE_FOLDER 7 +#define IDS_FILE_NOT_FOUND 9 +#define IDS_CONNECTION_TIMED_OUT 10 +#define IDS_ERROR_PARSING_XML 11 +#define IDS_NO_JNETLIB 13 +#define IDS_JNETLIB_MISSING 14 +#define IDS_NO_EXPAT 15 +#define IDS_EXPAT_MISSING 16 +#define IDS_CONNECTION_RESET 17 +#define IDS_WDAY_SUN 28 +#define IDS_WDAY_MON 29 +#define IDS_WDAY_TUE 30 +#define IDS_WDAY_WED 31 +#define IDS_WDAY_THU 32 +#define IDS_WDAY_FRI 33 +#define IDS_WDAY_SAT 34 +#define IDS_MONTH_JAN 35 +#define IDS_MONTH_FEB 36 +#define IDS_MONTH_MAR 37 +#define IDS_MONTH_APR 38 +#define IDS_MONTH_MAY 39 +#define IDS_MONTH_JUN 40 +#define IDS_MONTH_JUL 41 +#define IDS_MONTH_AUG 42 +#define IDS_MONTH_SEP 43 +#define IDS_MONTH_OCT 44 +#define IDS_MONTH_NOV 45 +#define IDS_MONTH_DEC 46 +#define IDS_INVALID_LINK 54 +#define IDS_CONNECTION_RESET_BY_PEER 55 +#define IDS_DOWNLOADING_PERCENT 56 +#define IDS_SOURCE 57 +#define IDS_ITEM 58 +#define IDS_TITLE 58 +#define IDS_PROGRESS 59 +#define IDS_PATH 60 +#define IDS_PERM_DELETE_ARE_YOU_SURE 61 +#define IDS_PERM_DELETE_THESE_ARE_YOU_SURE 62 +#define IDS_DELETION 63 +#define IDS_CLEAR_ALL_FINISHED_DOWNLOADS 64 +#define IDS_CLEAN_UP_LIST 65 +#define IDS_DONE 66 +#define IDS_SAVE 67 +#define IDS_REQUIRES_INTERNET_CONNECTION_ENSURE_CONNECTION 68 +#define IDS_ADD_TO_DOWNLOADS 69 +#define IDS_SURE_YOU_WANT_TO_REMOVE_THIS 74 +#define IDS_CONFIRM 75 +#define IDS_CANCEL_DOWNLOADS_AND_QUIT 76 +#define IDS_CONFIRM_QUIT 77 +#define IDS_DELETEFAILED 80 +#define IDS_SORRY 81 +#define IDS_DATE 82 +#define IDS_N_A 83 +#define IDS_SIZE 84 +#define IDD_DOWNLOADS 107 +#define IDR_MENU1 112 +#define IDR_VIEW_DOWNLOAD_ACCELERATORS 122 +#define IDB_TREEITEM_DOWNLOADS 126 +#define IDB_TREEITEM_DOWNLOADS1 131 +#define IDS_DOWNLOAD_SUCCESS 134 +#define IDS_DOWNLOAD_FAILURE 135 +#define IDS_DOWNLOAD_CANCELED 136 +#define IDB_TREEITEM_DOWNLOADS2 136 +#define IDB_BITMAP2 137 +#define IDB_TREEITEM_DOWNLOADS3 137 +#define IDS_DOWNLOAD_PENDING 138 +#define IDS_DOWNLOADING_PROGRESS 139 +#define IDS_DOWNLOADING_COMPLETE 140 +#define IDC_CUSTOM 1000 +#define IDC_DELETE 1011 +#define IDC_DOWNLOADLIST 1029 +#define IDC_STATICDOWNLOADLOCATION 1034 +#define IDC_STATICAUTODOWNLOAD 1035 +#define IDC_ADD 1044 +#define IDC_EDIT 1045 +#define IDC_PLAY 1047 +#define IDC_ENQUEUE 1048 +#define IDC_DOWNLOAD 1050 +#define IDC_CANCEL 1051 +#define IDC_BROWSE 1051 +#define IDC_ENQUEUE2 1051 +#define IDC_BROWSER 1057 +#define IDC_STATUS 1058 +#define IDC_DOWNLOADLOCATION 1059 +#define IDC_SETTINGSBOX 1060 +#define IDC_CLEANUP 1060 +#define IDC_PLAYACTION 1065 +#define IDC_ENQUEUEACTION 1066 +#define ID_PLAYMEDIA_IDC 40018 +#define IDC_REMOVE 40024 +#define ID_Menu 40026 +#define IDC_INFOBOX 40028 +#define IDC_SELECTALL 40030 +#define ID_NAVIGATION_PREFERENCES 40032 +#define ID_NAVIGATION_HELP 40033 +#define ID_DOWNLOADS_EXPLORERITEMFOLDER 40034 +#define ID_ENQUEUE 40036 +#define ID_DOWNLOADS_CANCELDOWNLOAD 40043 +#define IDS_PLUGIN_NAME 65534 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 141 +#define _APS_NEXT_COMMAND_VALUE 40044 +#define _APS_NEXT_CONTROL_VALUE 1068 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/Library/ml_downloads/resources/downloadIcon.bmp b/Src/Plugins/Library/ml_downloads/resources/downloadIcon.bmp Binary files differnew file mode 100644 index 00000000..ab762e4f --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/resources/downloadIcon.bmp diff --git a/Src/Plugins/Library/ml_downloads/resources/downloadIcon.png b/Src/Plugins/Library/ml_downloads/resources/downloadIcon.png Binary files differnew file mode 100644 index 00000000..ff92ea88 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/resources/downloadIcon.png diff --git a/Src/Plugins/Library/ml_downloads/resources/downloadIcon1.bmp b/Src/Plugins/Library/ml_downloads/resources/downloadIcon1.bmp Binary files differnew file mode 100644 index 00000000..4fef75f4 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/resources/downloadIcon1.bmp diff --git a/Src/Plugins/Library/ml_downloads/resources/downloadIcon2.bmp b/Src/Plugins/Library/ml_downloads/resources/downloadIcon2.bmp Binary files differnew file mode 100644 index 00000000..4b59943a --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/resources/downloadIcon2.bmp diff --git a/Src/Plugins/Library/ml_downloads/resources/downloadIcon3.bmp b/Src/Plugins/Library/ml_downloads/resources/downloadIcon3.bmp Binary files differnew file mode 100644 index 00000000..4e18a399 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/resources/downloadIcon3.bmp diff --git a/Src/Plugins/Library/ml_downloads/util.cpp b/Src/Plugins/Library/ml_downloads/util.cpp new file mode 100644 index 00000000..9a8a7927 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/util.cpp @@ -0,0 +1,255 @@ +#include "main.h" +#include "./util.h" +#include "api__ml_downloads.h" + +#include <strsafe.h> + +LPWSTR Plugin_MallocString(size_t cchLen) +{ + return (LPWSTR)calloc(cchLen, sizeof(WCHAR)); +} + +void Plugin_FreeString(LPWSTR pszString) +{ + if (NULL != pszString) + { + free(pszString); + } +} + +LPWSTR Plugin_ReAllocString(LPWSTR pszString, size_t cchLen) +{ + return (LPWSTR)realloc(pszString, sizeof(WCHAR) * cchLen); +} + +LPWSTR Plugin_CopyString(LPCWSTR pszSource) +{ + if (NULL == pszSource) + return NULL; + + INT cchSource = lstrlenW(pszSource) + 1; + + LPWSTR copy = Plugin_MallocString(cchSource); + if (NULL != copy) + { + CopyMemory(copy, pszSource, sizeof(WCHAR) * cchSource); + } + return copy; +} + +LPSTR Plugin_MallocAnsiString(size_t cchLen) +{ + return (LPSTR)calloc(cchLen, sizeof(CHAR)); +} + +LPSTR Plugin_CopyAnsiString(LPCSTR pszSource) +{ + if (NULL == pszSource) + return NULL; + + INT cchSource = lstrlenA(pszSource) + 1; + + LPSTR copy = Plugin_MallocAnsiString(cchSource); + if (NULL != copy) + { + CopyMemory(copy, pszSource, sizeof(CHAR) * cchSource); + } + return copy; + +} +void Plugin_FreeAnsiString(LPSTR pszString) +{ + Plugin_FreeString((LPWSTR)pszString); +} + +LPSTR Plugin_WideCharToMultiByte(UINT codePage, DWORD dwFlags, LPCWSTR lpWideCharStr, INT cchWideChar, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar) +{ + INT cchBuffer = WideCharToMultiByte(codePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar); + if (0 == cchBuffer) return NULL; + + LPSTR buffer = Plugin_MallocAnsiString(cchBuffer); + if (NULL == buffer) return NULL; + + if (0 == WideCharToMultiByte(codePage, dwFlags, lpWideCharStr, cchWideChar, buffer, cchBuffer, lpDefaultChar, lpUsedDefaultChar)) + { + Plugin_FreeAnsiString(buffer); + return NULL; + } + return buffer; +} + +LPWSTR Plugin_MultiByteToWideChar(UINT codePage, DWORD dwFlags, LPCSTR lpMultiByteStr, INT cbMultiByte) +{ + if (NULL == lpMultiByteStr) return NULL; + INT cchBuffer = MultiByteToWideChar(codePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0); + if (NULL == cchBuffer) return NULL; + + if (cbMultiByte > 0) cchBuffer++; + + LPWSTR buffer = Plugin_MallocString(cchBuffer); + if (NULL == buffer) return NULL; + + if (0 == MultiByteToWideChar(codePage, dwFlags, lpMultiByteStr, cbMultiByte, buffer, cchBuffer)) + { + Plugin_FreeString(buffer); + return NULL; + } + + if (cbMultiByte > 0) + { + buffer[cchBuffer - 1] = L'\0'; + } + return buffer; +} + + +LPWSTR Plugin_DuplicateResString(LPCWSTR pszResource) +{ + return (IS_INTRESOURCE(pszResource)) ? + (LPWSTR)pszResource : + Plugin_CopyString(pszResource); +} + +void Plugin_FreeResString(LPWSTR pszResource) +{ + if (!IS_INTRESOURCE(pszResource)) + Plugin_FreeString(pszResource); +} + +HRESULT Plugin_CopyResString(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszString) +{ + if (NULL == pszBuffer) + return E_INVALIDARG; + + HRESULT hr = S_OK; + + if (NULL == pszString) + { + pszBuffer[0] = L'\0'; + } + else if (IS_INTRESOURCE(pszString)) + { + if (NULL == WASABI_API_LNG) + hr = E_FAIL; + else + WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pszString, pszBuffer, cchBufferMax); + } + else + { + hr = StringCchCopy(pszBuffer, cchBufferMax, pszString); + } + return hr; +} + +void Plugin_SafeRelease(IUnknown *pUnk) +{ + if (NULL != pUnk) + pUnk->Release(); +} + +HRESULT Plugin_FileExtensionFromUrl(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszUrl, LPCWSTR defaultExtension) +{ + LPCWSTR cursor = pszUrl; + while (L'\0' != *cursor && L'?' != *cursor) + cursor = CharNext(cursor); + + LPCWSTR end = cursor; + + while (cursor != pszUrl && L'/' != *cursor && L'\\' != *cursor && L'.' != *cursor) + cursor = CharPrev(pszUrl, cursor); + + if (L'.' == *cursor && cursor != pszUrl) + { + if (CharNext(cursor) < end) + { + INT cchExtension = (INT)(INT_PTR)(end - cursor); + return StringCchCopyN(pszBuffer, cchBufferMax, cursor, cchExtension); + } + } + + return StringCchCopy(pszBuffer, cchBufferMax, defaultExtension); +} + +void Plugin_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); + } +} + +INT Plugin_CleanDirectory(LPWSTR pszPath) +{ + if (NULL == pszPath) + return 0; + + INT cchPath = lstrlen(pszPath); + LPWSTR cursor = pszPath + cchPath; + while (cursor-- != pszPath && (L' ' == *cursor || L'.' == *cursor)) + *cursor = L'\0'; + + return (cchPath - (INT)(INT_PTR)(cursor - pszPath) - 1); +} + +HRESULT Plugin_EnsurePathExist(LPCWSTR pszDirectory) +{ + DWORD ec = ERROR_SUCCESS; + + UINT errorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); + + if (0 == CreateDirectory(pszDirectory, NULL)) + { + ec = GetLastError(); + if (ERROR_PATH_NOT_FOUND == ec) + { + LPCWSTR pszBlock = pszDirectory; + WCHAR szBuffer[MAX_PATH] = {0}; + + LPCTSTR pszCursor = PathFindNextComponent(pszBlock); + ec = (pszCursor == pszBlock || S_OK != StringCchCopyN(szBuffer, ARRAYSIZE(szBuffer), pszBlock, (pszCursor - pszBlock))) ? + ERROR_INVALID_NAME : ERROR_SUCCESS; + + pszBlock = pszCursor; + + while (ERROR_SUCCESS == ec && NULL != (pszCursor = PathFindNextComponent(pszBlock))) + { + if (pszCursor == pszBlock || S_OK != StringCchCatN(szBuffer, ARRAYSIZE(szBuffer), pszBlock, (pszCursor - pszBlock))) + ec = ERROR_INVALID_NAME; + + if (ERROR_SUCCESS == ec && !CreateDirectory(szBuffer, NULL)) + { + ec = GetLastError(); + if (ERROR_ALREADY_EXISTS == ec) ec = ERROR_SUCCESS; + } + pszBlock = pszCursor; + } + } + + if (ERROR_ALREADY_EXISTS == ec) + ec = ERROR_SUCCESS; + } + + SetErrorMode(errorMode); + SetLastError(ec); + return HRESULT_FROM_WIN32(ec); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_downloads/version.rc2 b/Src/Plugins/Library/ml_downloads/version.rc2 new file mode 100644 index 00000000..f6e65323 --- /dev/null +++ b/Src/Plugins/Library/ml_downloads/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,33,0,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Media Library Plug-in" + VALUE "FileVersion", "1,33,0,0" + VALUE "InternalName", "Nullsoft Downloads" + VALUE "LegalCopyright", "Copyright © 2010-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "ml_downloads.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END |