view_pl.cpp 79 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885
  1. #include <strsafe.h>
  2. #include <shlobj.h>
  3. #include <direct.h>
  4. #include <atomic>
  5. #include "main.h"
  6. #include "Playlist.h"
  7. #include "resource.h"
  8. #include "nu/listview.h"
  9. #include "CurrentPlaylist.h"
  10. #include "nu/AutoCharFn.h"
  11. #include "../../General/gen_ml/ml.h"
  12. #include "../../General/gen_ml/ml_ipc.h"
  13. #include "SendTo.h"
  14. #include "PlaylistView.h"
  15. #include "PlaylistDirectoryCallback.h"
  16. #include "api__ml_playlists.h"
  17. #include "../../General/gen_ml/menufucker.h"
  18. #include "nu/menushortcuts.h"
  19. #include "../../General/gen_ml/ml_ipc_0313.h"
  20. #include "ml_local/api_mldb.h"
  21. #include "ml_pmp/pmp.h"
  22. #include "replicant/nswasabi/ReferenceCounted.h"
  23. #include "replicant/nx/win/nxstring.h"
  24. #include "playlist/plstring.h"
  25. #include "../nu/AutoChar.h"
  26. #include "../nu/AutoWide.h"
  27. #include "../nu/AutoLock.h"
  28. #include "../replicant/nu/AutoLock.h"
  29. #include "../../../WAT/wa_logger.h"
  30. #include <iostream>
  31. #include <fstream>
  32. #include "../../../Components/wac_network/wac_network_http_receiver_api.h"
  33. #include "../../../Components/wac_downloadManager/wac_downloadManager_api.h"
  34. //#include "../replicant/replicant/metadata/metadata.h"
  35. using namespace Nullsoft::Utility;
  36. #define SIMULTANEOUS_DOWNLOADS 1
  37. #define PLAYLIST_DOWNLOAD_SUBFOLDER "\\Winamp_Library\\"
  38. #define WINAMP_REDIRECT_LINK_PROXY_FILE L"http://client.winamp.com/fileproxy?destination="
  39. std::vector<DownloadToken> plDownloads;
  40. LockGuard itemsPlaylistQueueLock;
  41. Playlist currentPlaylist;
  42. wchar_t currentPlaylistFilename[FILENAME_SIZE] = {0};
  43. wchar_t currentPlaylistTitle[FILETITLE_SIZE] = {0};
  44. wchar_t current_playing[FILENAME_SIZE] = {0};
  45. W_ListView playlist_list;
  46. static GUID playlist_guid = INVALID_GUID;
  47. int IPC_LIBRARY_SENDTOMENU;
  48. int we_are_drag_and_dropping = 0;
  49. static void AutoSizePlaylistColumns();
  50. static SendToMenu sendTo;
  51. viewButtons view = {0};
  52. typedef enum
  53. {
  54. SCROLLDIR_NONE = 0,
  55. SCROLLDIR_UP = 1,
  56. SCROLLDIR_DOWN = -1,
  57. } SCROLLDIR;
  58. #define SCROLLTIMER_ID 100
  59. static INT scrollDelay = 0;
  60. static INT scrollTimerElapse = 0;
  61. static int scrollDirection = SCROLLDIR_NONE;
  62. void UpdatePlaylistTime(HWND hwndDlg);
  63. static bool opened = false;
  64. static bool changed = false;
  65. static bool loaded = false;
  66. HWND activeHWND = 0;
  67. HWND saveHWND = 0;
  68. int groupBtn = 1;
  69. int customAllowed = 0;
  70. int enqueuedef = 0;
  71. void Changed( bool _changed = true )
  72. {
  73. changed = _changed;
  74. EnableWindow( saveHWND, changed );
  75. }
  76. void SyncPlaylist()
  77. {
  78. if ( opened )
  79. {
  80. playlist_list.SetVirtualCount( (INT)currentPlaylist.GetNumItems() );
  81. playlist_list.RefreshAll();
  82. UpdatePlaylistTime( GetParent( playlist_list.getwnd() ) );
  83. if ( !current_playing[ 0 ] )
  84. lstrcpynW( current_playing, (wchar_t *)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_PLAYING_FILENAME ), FILENAME_SIZE );
  85. PostMessage( activeHWND, WM_APP + 103, (WPARAM)current_playing, 1 );
  86. }
  87. }
  88. void SyncMenuWithAccelerators( HWND hwndDlg, HMENU menu )
  89. {
  90. HACCEL szAccel[ 24 ] = { 0 };
  91. INT c = WASABI_API_APP->app_getAccelerators( hwndDlg, szAccel, sizeof( szAccel ) / sizeof( szAccel[ 0 ] ), FALSE );
  92. AppendMenuShortcuts( menu, szAccel, c, MSF_REPLACE );
  93. }
  94. void UpdateMenuItems( HWND hwndDlg, HMENU menu )
  95. {
  96. bool swapPlayEnqueue = false;
  97. if ( g_config->ReadInt( L"enqueuedef", 0 ) == 1 )
  98. {
  99. SwapPlayEnqueueInMenu( menu );
  100. swapPlayEnqueue = true;
  101. }
  102. SyncMenuWithAccelerators( hwndDlg, menu );
  103. if ( swapPlayEnqueue )
  104. SwapPlayEnqueueInMenu( menu );
  105. }
  106. void TagEditor( HWND hwnd )
  107. {
  108. wchar_t fn[ 1024 ] = { 0 };
  109. wchar_t ft[ 1024 ] = { 0 };
  110. int v = playlist_list.GetCount();
  111. for ( int x = 0; x < v; x++ )
  112. {
  113. if ( playlist_list.GetSelected( x ) )
  114. {
  115. currentPlaylist.GetItem( x, fn, 1024 );
  116. infoBoxParamW p;
  117. p.filename = fn;
  118. p.parent = hwnd;
  119. if ( SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&p, IPC_INFOBOXW ) )
  120. break;
  121. int length = -1;
  122. mediaLibrary.GetFileInfo( fn, ft, 1024, &length );
  123. currentPlaylist.SetItemTitle( x, ft );
  124. currentPlaylist.SetItemLengthMilliseconds( x, length * 1000 );
  125. playlist_list.RefreshItem( x );
  126. Changed();
  127. }
  128. }
  129. MSG msg;
  130. while ( PeekMessage( &msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE ) ); //eat return
  131. }
  132. void myOpenURL(HWND hwnd, wchar_t *loc);
  133. void Playlist_GenerateHtmlPlaylist(void)
  134. {
  135. FILE *fp = 0;
  136. wchar_t filename[MAX_PATH], tp[MAX_PATH] = {0};
  137. if (!GetTempPathW(MAX_PATH,tp))
  138. StringCchCopyW(tp, MAX_PATH, L".");
  139. if ( GetTempFileNameW( tp, L"WHT", 0, filename ) )
  140. {
  141. DeleteFileW( filename );
  142. StringCchCatW( filename, MAX_PATH, L".html" );
  143. }
  144. else
  145. StringCchCopyW(filename, MAX_PATH, L"wahtml_tmp.html");
  146. fp = _wfopen(filename, L"wt");
  147. if ( !fp )
  148. {
  149. //MessageBox(activeHWND, IDS_HTML_ERROR_WRITE, IDS_ERROR, MB_OK | MB_ICONWARNING);
  150. return;
  151. }
  152. fprintf(fp, "<!DOCTYPE html>\n"
  153. "<html><head>\n"
  154. "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n"
  155. "<meta name=\"generator\" content=\"Winamp 5.9\">\n"
  156. "<style type=\"text/css\">body{background:#000040;font-family:arial,helvetica;font-size:9pt;font-weight:normal;}"
  157. ".name{margin-top:-1em;margin-left:15px;font-size:40pt;color:#004080;text-align:left;font-weight:900;}"
  158. ".name-small{margin-top:-3em;margin-left:140px;font-size:22pt;color:#E1E1E1;text-align:left;}"
  159. "table{font-size:9pt;color:#004080;text-align:left;border-width:0px;padding:0px;letter-spacing:normal;}"
  160. "hr{border:0;background-color:#FFBF00;height:1px;}"
  161. "ol{color:#FFFFFF;font-size:11pt;}"
  162. "table{margin-left:15px;color:#409FFF;border-width:0px;}"
  163. ".val{color:#FFBF00;}"
  164. ".header{color:#FFBF00;font-size:14pt;}"
  165. "</style>\n"
  166. "<title>Winamp Generated PlayList</title></head>\n"
  167. "<body>"
  168. "<div>"
  169. "<div class=\"name\"><p>WINAMP</p></div>"
  170. "<div class=\"name-small\"><p>playlist</p></div>"
  171. "</div>"
  172. "<hr><div>\n"
  173. "<table><tr><td>\n");
  174. int x, t = playlist_list.GetCount(), t_in_pl = 0, n_un = 0;
  175. for ( x = 0; x < t; x++ )
  176. {
  177. int a = currentPlaylist.GetItemLengthMilliseconds( x );
  178. if ( a >= 0 )
  179. t_in_pl += ( a / 1000 );
  180. else
  181. n_un++;
  182. }
  183. if ( t != n_un )
  184. {
  185. int old_t_in_pl = t_in_pl;
  186. t_in_pl += ( n_un * t_in_pl ) / ( t - n_un );
  187. fprintf( fp, "<span class=\"val\">%d</span> track%s in playlist, ", t, t == 1 ? "" : "s" );
  188. fprintf( fp, "average track length: <span class=\"val\">%d:%02d", old_t_in_pl / ( t - n_un ) / 60, ( old_t_in_pl / ( t - n_un ) ) % 60 );
  189. fprintf( fp, "</span><br>%slaylist length: ", n_un ? "Estimated p" : "P" );
  190. if ( t_in_pl / 3600 )
  191. {
  192. fprintf( fp, "<span class=\"val\">%d</span> hour%s ", t_in_pl / 3600, t_in_pl / 3600 == 1 ? "" : "s" );
  193. t_in_pl %= 3600;
  194. }
  195. if ( t_in_pl / 60 )
  196. {
  197. fprintf( fp, "<span class=\"val\">%d</span> minute%s ", t_in_pl / 60, t_in_pl / 60 == 1 ? "" : "s" );
  198. t_in_pl %= 60;
  199. }
  200. fprintf( fp, "<span class=\"val\">%d</span> second%s %s", t_in_pl, t_in_pl == 1 ? "" : "s", n_un ? "<br>(" : "" );
  201. if ( n_un )
  202. fprintf( fp, "<span class=\"val\">%d</span> track%s of unknown length)", n_un, n_un == 1 ? "" : "s" );
  203. fprintf( fp,
  204. "<br>Right-click <a href=\"file://%s\">here</a> to save this HTML file."
  205. "</td></tr>",
  206. (char *)AutoChar( filename, CP_UTF8 ) );
  207. }
  208. else
  209. {
  210. fprintf( fp, "There are no tracks in the current playlist.<br>" );
  211. }
  212. fprintf(fp, "</table></div>\n");
  213. if ( t > 0 )
  214. {
  215. fprintf( fp, "<blockquote><span class=\"header\">Playlist files:</span><ol>" );
  216. for ( x = 0; x < t; x++ )
  217. {
  218. wchar_t ft[ FILETITLE_SIZE ] = { 0 };
  219. currentPlaylist.GetItemTitle( x, ft, FILENAME_SIZE );
  220. AutoChar narrowFt( ft, CP_UTF8 );
  221. char *p = narrowFt;
  222. int l = currentPlaylist.GetItemLengthMilliseconds( x );
  223. if ( l > 0 )
  224. l /= 1000;
  225. fprintf( fp, "<li>" );
  226. while ( p && *p )
  227. {
  228. if ( *p == '&' )
  229. fprintf( fp, "&amp;" );
  230. else if ( *p == '<' )
  231. fprintf( fp, "&lt;" );
  232. else if ( *p == '>' )
  233. fprintf( fp, "&gt;" );
  234. else if ( *p == '\'' )
  235. fprintf( fp, "&#39;" );
  236. else if ( *p == '"' )
  237. fprintf( fp, "&quot;" );
  238. else
  239. fputc( *p, fp );
  240. p++;
  241. }
  242. if ( l > 0 )
  243. fprintf( fp, " (%d:%02d) \n", l / 60, l % 60 );
  244. else
  245. fprintf( fp, " \n" );
  246. }
  247. fprintf( fp, "</ol></blockquote>" );
  248. }
  249. fprintf(fp, "<hr><br></body></html>");
  250. fclose(fp);
  251. myOpenURL(activeHWND, filename);
  252. }
  253. void Playlist_ResetSelected()
  254. {
  255. int i = playlist_list.GetCount();
  256. while ( i-- )
  257. {
  258. if ( playlist_list.GetSelected( i ) )
  259. currentPlaylist.ClearCache( i );
  260. }
  261. Changed();
  262. SyncPlaylist();
  263. }
  264. void Playlist_FindSelected()
  265. {
  266. if ( playlist_list.GetSelectionMark() >= 0 )
  267. {
  268. int l = playlist_list.GetCount();
  269. for ( int i = 0; i < l; i++ )
  270. {
  271. if ( playlist_list.GetSelected( i ) )
  272. WASABI_API_EXPLORERFINDFILE->AddFile( (wchar_t *)currentPlaylist.ItemName( i ) );
  273. }
  274. WASABI_API_EXPLORERFINDFILE->ShowFiles();
  275. }
  276. }
  277. void Playlist_DeleteSelected( int selected )
  278. {
  279. selected = !!selected; // convert to 0 or 1
  280. int i = playlist_list.GetCount();
  281. while ( i-- )
  282. {
  283. if ( !( playlist_list.GetSelected( i ) ^ selected ) )
  284. {
  285. currentPlaylist.Remove( i );
  286. playlist_list.Unselect( i );
  287. }
  288. }
  289. Changed();
  290. SyncPlaylist();
  291. }
  292. void Playlist_RecycleSelected( HWND hwndDlg, int selected )
  293. {
  294. SHFILEOPSTRUCTW fileOp;
  295. fileOp.hwnd = hwndDlg;
  296. fileOp.wFunc = FO_DELETE;
  297. fileOp.pFrom = 0;
  298. fileOp.pTo = 0;
  299. fileOp.fFlags = SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_USES_RECYCLEBIN ) ? FOF_ALLOWUNDO : 0;
  300. fileOp.fAnyOperationsAborted = 0;
  301. fileOp.hNameMappings = 0;
  302. fileOp.lpszProgressTitle = 0;
  303. selected = !!selected; // convert to 0 or 1
  304. int i = playlist_list.GetCount();
  305. wchar_t *files = new wchar_t[ i * ( MAX_PATH + 1 ) + 1 ]; // need room for each file name, null terminated. then have to null terminate the whole list
  306. if ( files )
  307. {
  308. wchar_t *curFile = files;
  309. for ( int x = 0; x < i; x++ )
  310. {
  311. if ( !( playlist_list.GetSelected( x ) ^ selected ) )
  312. {
  313. lstrcpynW( curFile, currentPlaylist.ItemName( x ), MAX_PATH );
  314. curFile += wcslen( currentPlaylist.ItemName( x ) ) + 1;
  315. }
  316. }
  317. if ( curFile != files )
  318. {
  319. curFile[ 0 ] = 0; // null terminate
  320. fileOp.pFrom = files;
  321. if ( SHFileOperationW( &fileOp ) )
  322. {
  323. wchar_t titleStr[ 32 ] = { 0 };
  324. MessageBox( hwndDlg, WASABI_API_LNGSTRINGW( IDS_ERROR_DELETING_FILES ), WASABI_API_LNGSTRINGW_BUF( IDS_ERROR, titleStr, 32 ), MB_OK );
  325. }
  326. else if ( !fileOp.fAnyOperationsAborted )
  327. {
  328. while ( i-- )
  329. {
  330. if ( !( playlist_list.GetSelected( i ) ^ selected ) )
  331. {
  332. currentPlaylist.Remove( i );
  333. playlist_list.Unselect( i );
  334. }
  335. }
  336. }
  337. }
  338. delete[] files;
  339. }
  340. else // if malloc failed ... maybe because there'l_enqueue_file too many items.
  341. {
  342. while ( i-- )
  343. {
  344. if ( !( playlist_list.GetSelected( i ) ^ selected ) )
  345. {
  346. fileOp.pFrom = currentPlaylist.ItemName( i );
  347. if ( SHFileOperationW( &fileOp ) )
  348. continue;
  349. if ( fileOp.fAnyOperationsAborted )
  350. break;
  351. currentPlaylist.Remove( i );
  352. playlist_list.Unselect( i );
  353. }
  354. }
  355. }
  356. Changed();
  357. SyncPlaylist();
  358. }
  359. int GetSelectedLength()
  360. {
  361. int length = 0;
  362. int selected = -1;
  363. while ( ( selected = playlist_list.GetNextSelected( selected ) ) != -1 )
  364. {
  365. int thisLen = currentPlaylist.GetItemLengthMilliseconds( selected );
  366. if ( thisLen > 0 )
  367. length += thisLen / 1000;
  368. }
  369. return length;
  370. }
  371. int GetTotalLength()
  372. {
  373. int length = 0;
  374. int len = playlist_list.GetCount();
  375. for ( int i = 0; i < len; i++ )
  376. {
  377. int thisLen = currentPlaylist.GetItemLengthMilliseconds( i );
  378. if ( thisLen > 0 )
  379. length += thisLen / 1000;
  380. }
  381. return length;
  382. }
  383. void FormatLength( wchar_t *str, int length, int buf_len )
  384. {
  385. if ( !length )
  386. lstrcpynW( str, L"0:00", buf_len );
  387. else if ( length < 60 * 60 )
  388. StringCchPrintfW( str, buf_len, L"%d:%02d", length / 60, length % 60 );
  389. else
  390. {
  391. int total_days = length / ( 60 * 60 * 24 );
  392. if ( total_days )
  393. {
  394. length -= total_days * 60 * 60 * 24;
  395. StringCchPrintfW( str, buf_len, L"%d %s+%d:%02d:%02d", total_days, WASABI_API_LNGSTRINGW( ( total_days == 1 ? IDS_DAY : IDS_DAYS ) ), length / 60 / 60, ( length / 60 ) % 60, length % 60 );
  396. }
  397. else
  398. StringCchPrintfW( str, buf_len, L"%d:%02d:%02d", length / 60 / 60, ( length / 60 ) % 60, length % 60 );
  399. }
  400. }
  401. void UpdatePlaylistTime(HWND hwndDlg)
  402. {
  403. wchar_t str[64] = {0}, str2[32] = {0};
  404. int selitems = playlist_list.GetSelectedCount();
  405. int seltime = GetSelectedLength(), ttime = GetTotalLength();
  406. FormatLength(str, seltime, 64);
  407. FormatLength(str2, ttime, 32);
  408. wchar_t buf2[128] = {0}, sStr[16] = {0};
  409. if ( selitems )
  410. StringCchPrintf( buf2, 128, WASABI_API_LNGSTRINGW( IDS_X_OF_X_SELECTED ), selitems, playlist_list.GetCount(), WASABI_API_LNGSTRINGW_BUF( playlist_list.GetCount() == 1 ? IDS_ITEM : IDS_ITEMS_LOWER, sStr, 16 ), str, str2 );
  411. else
  412. StringCchPrintf( buf2, 128, WASABI_API_LNGSTRINGW( IDS_X_SELECTED ), playlist_list.GetCount(), WASABI_API_LNGSTRINGW_BUF( playlist_list.GetCount() == 1 ? IDS_ITEM : IDS_ITEMS_LOWER, sStr, 16 ), str2 );
  413. SetDlgItemText(hwndDlg, IDC_PLSTATUS, buf2);
  414. }
  415. static wchar_t *BuildFilenameList( int is_all )
  416. {
  417. wchar_t filename[ MAX_PATH ] = { 0 };
  418. size_t len = MAX_PATH;
  419. wchar_t *str = (wchar_t *)calloc( len, sizeof( wchar_t ) );
  420. size_t sofar = 0;
  421. int numTracks = playlist_list.GetCount();
  422. for ( int i = 0; i < numTracks; i++ )
  423. {
  424. if ( is_all || playlist_list.GetSelected( i ) )
  425. {
  426. if ( currentPlaylist.GetItem( i, filename, MAX_PATH ) )
  427. {
  428. int filenameLen = lstrlen( filename ) + 1;
  429. if ( ( filenameLen + sofar ) > len )
  430. {
  431. int newLen = sofar * 2; // add some cushion
  432. wchar_t *newStr = (wchar_t *)realloc( str, newLen * sizeof( wchar_t ) );
  433. if ( !newStr )
  434. {
  435. newLen = sofar + filenameLen;
  436. // try the minimum possible size to get this to work
  437. newStr = (wchar_t *)realloc( str, newLen * sizeof( wchar_t ) );
  438. if ( !newStr )
  439. {
  440. free( str );
  441. return 0;
  442. }
  443. }
  444. str = newStr;
  445. }
  446. lstrcpyn( str + sofar, filename, filenameLen );
  447. sofar += filenameLen;
  448. }
  449. }
  450. }
  451. *( str + sofar ) = 0;
  452. return str;
  453. }
  454. void PlaySelection( int enqueue, int is_all )
  455. {
  456. if ( !enqueue )
  457. SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_DELETE );
  458. int numTracks = playlist_list.GetCount();
  459. for ( int i = 0; i < numTracks; i++ )
  460. {
  461. if ( is_all || playlist_list.GetSelected( i ) )
  462. {
  463. const wchar_t *filename = currentPlaylist.ItemName( i );
  464. if ( filename )
  465. {
  466. enqueueFileWithMetaStructW l_enqueue_file;
  467. std::map<std::wstring, std::wstring> &l_extended_infos = currentPlaylist.entries[ i ]->_extended_infos;
  468. wa::strings::wa_string l_original_filename( filename );
  469. if ( !currentPlaylist.entries[ i ]->isLocal() && !l_extended_infos.empty() && l_extended_infos.count( L"ext" ) == 1 && l_original_filename.contains( "://" ) )
  470. {
  471. wa::strings::wa_string l_ext( "." );
  472. l_ext.append( ( *l_extended_infos.find( L"ext" ) ).second );
  473. l_ext.toUpper();
  474. wa::strings::wa_string l_filename( WINAMP_REDIRECT_LINK_PROXY_FILE );
  475. l_filename.append( filename );
  476. wa::strings::wa_string l_upper_case_filename( filename );
  477. l_upper_case_filename.toUpper();
  478. if ( !l_upper_case_filename.contains( l_ext.GetW() ) )
  479. {
  480. if ( !l_original_filename.contains( "?" ) )
  481. l_filename.append( "?" );
  482. else
  483. l_filename.append( "&" );
  484. l_filename.append( "ext=" );
  485. l_filename.append( l_ext );
  486. }
  487. l_enqueue_file.filename = _wcsdup( l_filename.GetW().c_str() );
  488. l_enqueue_file.ext = _wcsdup( ( *l_extended_infos.find( L"ext" ) ).second.c_str() );
  489. }
  490. else
  491. {
  492. l_enqueue_file.filename = filename;
  493. l_enqueue_file.ext = NULL;
  494. }
  495. wa::strings::wa_string l_filename( l_enqueue_file.filename );
  496. if ( l_filename.contains( "://" ) )
  497. {
  498. wsprintfW( _log_message_w, L"The link '%s' will be played!", l_filename.GetW().c_str() );
  499. LOG_DEBUG( _log_message_w );
  500. }
  501. if ( currentPlaylist.IsCached( i ) )
  502. {
  503. l_enqueue_file.title = currentPlaylist.ItemTitle( i );
  504. plstring_retain( (wchar_t *)l_enqueue_file.title );
  505. l_enqueue_file.length = currentPlaylist.GetItemLengthMilliseconds( i ) / 1000;
  506. }
  507. else
  508. {
  509. l_enqueue_file.title = 0;
  510. l_enqueue_file.length = 0;
  511. }
  512. plstring_retain( (wchar_t *)l_enqueue_file.filename );
  513. SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&l_enqueue_file, IPC_PLAYFILEW_NDE_TITLE );
  514. }
  515. }
  516. }
  517. if ( !enqueue )
  518. {
  519. if ( is_all )
  520. {
  521. int pos = playlist_list.GetNextSelected( -1 );
  522. if ( pos != -1 )
  523. {
  524. SendMessage( plugin.hwndWinampParent, WM_WA_IPC, pos, IPC_SETPLAYLISTPOS );
  525. SendMessage( plugin.hwndWinampParent, WM_COMMAND, 40047, 0 ); // stop button, literally
  526. SendMessage( plugin.hwndWinampParent, WM_COMMAND, 40045, 0 ); // play button, literally
  527. return;
  528. }
  529. }
  530. SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_STARTPLAY );
  531. }
  532. }
  533. int playlist_Load( const wchar_t *playlistFileName )
  534. {
  535. currentPlaylist.Clear();
  536. return AGAVE_API_PLAYLISTMANAGER->Load( playlistFileName, &currentPlaylist );
  537. }
  538. LRESULT playlist_cloud_listview( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  539. {
  540. if ( uMsg == WM_NOTIFY )
  541. {
  542. LPNMHDR l = (LPNMHDR)lParam;
  543. switch ( l->code )
  544. {
  545. case TTN_SHOW:
  546. {
  547. LVHITTESTINFO lvh = {0};
  548. GetCursorPos(&lvh.pt);
  549. ScreenToClient(hwnd, &lvh.pt);
  550. ListView_SubItemHitTest(hwnd, &lvh);
  551. if ( cloud_avail && lvh.iItem != -1 && lvh.iSubItem == 1 )
  552. {
  553. LPTOOLTIPTEXTW tt = (LPTOOLTIPTEXTW)lParam;
  554. RECT r = { 0 };
  555. if ( lvh.iSubItem )
  556. ListView_GetSubItemRect( hwnd, lvh.iItem, lvh.iSubItem, LVIR_BOUNDS, &r );
  557. else
  558. {
  559. ListView_GetItemRect( hwnd, lvh.iItem, &r, LVIR_BOUNDS );
  560. r.right = r.left + ListView_GetColumnWidth( hwnd, 1 );
  561. }
  562. MapWindowPoints( hwnd, HWND_DESKTOP, (LPPOINT)&r, 2 );
  563. SetWindowPos( tt->hdr.hwndFrom, HWND_TOPMOST, r.right, r.top + 2, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE );
  564. return 1;
  565. }
  566. }
  567. break;
  568. case TTN_NEEDTEXTW:
  569. {
  570. LVHITTESTINFO lvh = {0};
  571. GetCursorPos(&lvh.pt);
  572. ScreenToClient(hwnd, &lvh.pt);
  573. ListView_SubItemHitTest(hwnd, &lvh);
  574. static wchar_t tt_buf2[256] = {L""};
  575. if ( cloud_avail && lvh.iItem != -1 && lvh.iSubItem == 1 )
  576. {
  577. LPNMTTDISPINFO lpnmtdi = (LPNMTTDISPINFO)lParam;
  578. static int last_item2 = -1;
  579. if ( last_item2 == lvh.iItem )
  580. {
  581. lpnmtdi->lpszText = tt_buf2;
  582. return 0;
  583. }
  584. wchar_t info[ 16 ] = { 0 };
  585. currentPlaylist.GetItemExtendedInfo( lvh.iItem, L"cloud_status", info, 16 );
  586. int status = _wtoi( info );
  587. if ( status == 4 )
  588. {
  589. WASABI_API_LNGSTRINGW_BUF( IDS_UPLOAD_TO_SOURCE, tt_buf2, ARRAYSIZE( tt_buf2 ) );
  590. }
  591. else
  592. {
  593. winampMediaLibraryPlugin *( *gp )( );
  594. gp = ( winampMediaLibraryPlugin * ( __cdecl * )( void ) )GetProcAddress( cloud_hinst, "winampGetMediaLibraryPlugin" );
  595. if ( gp )
  596. {
  597. winampMediaLibraryPlugin *mlplugin = gp();
  598. if ( mlplugin && ( mlplugin->version >= MLHDR_VER_OLD && mlplugin->version <= MLHDR_VER ) )
  599. {
  600. // TODO handle case when not in a device
  601. WASABI_API_LNGSTRINGW_BUF( IDS_TRACK_AVAILABLE, tt_buf2, ARRAYSIZE( tt_buf2 ) );
  602. wchar_t filepath[ 1024 ] = { 0 };
  603. currentPlaylist.GetItem( lvh.iItem, filepath, 1024 );
  604. nx_string_t *out_devicenames = 0;
  605. size_t num_names = mlplugin->MessageProc( 0x405, (INT_PTR)&filepath, (INT_PTR)&out_devicenames, 0 );
  606. if ( num_names > 0 )
  607. {
  608. for ( size_t i = 0; i < num_names; i++ )
  609. {
  610. if ( i > 0 )
  611. StringCchCatW( tt_buf2, ARRAYSIZE( tt_buf2 ), L", " );
  612. StringCchCatW( tt_buf2, ARRAYSIZE( tt_buf2 ), out_devicenames[ i ]->string );
  613. }
  614. }
  615. else
  616. {
  617. WASABI_API_LNGSTRINGW_BUF( IDS_UPLOAD_TO_SOURCE, tt_buf2, ARRAYSIZE( tt_buf2 ) );
  618. }
  619. if ( out_devicenames )
  620. free( out_devicenames );
  621. }
  622. }
  623. }
  624. last_item2 = lvh.iItem;
  625. lpnmtdi->lpszText = tt_buf2;
  626. // bit of a fiddle but it allows for multi-line tooltips
  627. //SendMessage(l->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 0);
  628. }
  629. else
  630. return CallWindowProcW((WNDPROC)GetPropW(hwnd, L"cloud_list_proc"), hwnd, uMsg, wParam, lParam);
  631. }
  632. return 0;
  633. }
  634. }
  635. return CallWindowProcW((WNDPROC)GetPropW(hwnd, L"cloud_list_proc"), hwnd, uMsg, wParam, lParam);
  636. }
  637. void playlist_UpdateButtonText( HWND hwndDlg, int _enqueuedef )
  638. {
  639. if ( groupBtn )
  640. {
  641. switch ( _enqueuedef )
  642. {
  643. case 1:
  644. SetDlgItemTextW( hwndDlg, IDC_PLAY, view.enqueue );
  645. customAllowed = FALSE;
  646. break;
  647. default:
  648. // v5.66+ - re-use the old predixis parts so the button can be used functionally via ml_enqplay
  649. // pass the hwnd, button id and plug-in id so the ml plug-in can check things as needed
  650. pluginMessage p = { ML_MSG_VIEW_BUTTON_HOOK_IN_USE, (INT_PTR)_enqueuedef, 0, 0 };
  651. wchar_t *pszTextW = (wchar_t *)SENDMLIPC( plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p );
  652. if ( pszTextW && pszTextW[ 0 ] != 0 )
  653. {
  654. // set this to be a bit different so we can just use one button and not the
  655. // mixable one as well (leaving that to prevent messing with the resources)
  656. SetDlgItemTextW( hwndDlg, IDC_PLAY, pszTextW );
  657. customAllowed = TRUE;
  658. }
  659. else
  660. {
  661. SetDlgItemTextW( hwndDlg, IDC_PLAY, view.play );
  662. customAllowed = FALSE;
  663. }
  664. break;
  665. }
  666. }
  667. }
  668. static void playlist_Init( HWND hwndDlg, LPARAM lParam )
  669. {
  670. HACCEL accel = WASABI_API_LOADACCELERATORSW( IDR_VIEW_PL_ACCELERATORS );
  671. if ( accel )
  672. WASABI_API_APP->app_addAccelerators( hwndDlg, &accel, 1, TRANSLATE_MODE_CHILD );
  673. if ( !view.play )
  674. SENDMLIPC( plugin.hwndLibraryParent, ML_IPC_GET_VIEW_BUTTON_TEXT, (WPARAM)&view );
  675. opened = true;
  676. loaded = false;
  677. activeHWND = hwndDlg;
  678. saveHWND = GetDlgItem(hwndDlg, IDC_SAVE_PL);
  679. Changed( false );
  680. cloud_avail = playlists_CloudAvailable();
  681. groupBtn = g_config->ReadInt( L"groupbtn", 1 );
  682. enqueuedef = ( g_config->ReadInt( L"enqueuedef", 0 ) == 1 );
  683. // force create this as it helps resolve some quirks with play / enqueue handling and
  684. // we cannot guarantee the button being on the dialog resource due to old lang packs.
  685. if ( !IsWindow( GetDlgItem( hwndDlg, IDC_ENQUEUE ) ) )
  686. {
  687. HWND newnd = CreateWindowEx( WS_EX_NOPARENTNOTIFY, L"button", view.enqueue, WS_CHILD | WS_TABSTOP | BS_OWNERDRAW, 0, 0, 0, 0, hwndDlg, (HMENU)IDC_ENQUEUE, plugin.hDllInstance, 0 );
  688. // make sure we're using an appropriate font for the display (may need to review this...)
  689. SendMessage( newnd, WM_SETFONT, (WPARAM)SendDlgItemMessage( hwndDlg, IDC_PLAY, WM_GETFONT, 0, 0 ), 0 );
  690. SetWindowPos( newnd, GetDlgItem( hwndDlg, IDC_PLAY ), 0, 0, 0, 0, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOSIZE | SWP_SHOWWINDOW );
  691. }
  692. // v5.66+ - re-use the old predixis parts so the button can be used functionally via ml_enqplay
  693. // pass the hwnd, button id and plug-in id so the ml plug-in can check things as needed
  694. pluginMessage p = { ML_MSG_VIEW_BUTTON_HOOK, (INT_PTR)hwndDlg, (INT_PTR)MAKELONG( IDC_CUSTOM, IDC_ENQUEUE ), (INT_PTR)L"ml_playlists" };
  695. wchar_t *pszTextW = (wchar_t *)SENDMLIPC( plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p );
  696. if ( pszTextW && pszTextW[ 0 ] != 0 )
  697. {
  698. // set this to be a bit different so we can just use one button and not the
  699. // mixable one as well (leaving that to prevent messing with the resources)
  700. customAllowed = TRUE;
  701. SetDlgItemTextW( hwndDlg, IDC_CUSTOM, pszTextW );
  702. }
  703. else
  704. {
  705. customAllowed = FALSE;
  706. }
  707. // override the button text to save a bit more space
  708. if ( !g_config->ReadInt( L"pltextbuttons", 0 ) )
  709. {
  710. SetDlgItemText( hwndDlg, IDC_ADD, L"+" );
  711. SetDlgItemText( hwndDlg, IDC_REM, L"\u2212" );
  712. }
  713. /* skin dialog */
  714. MLSKINWINDOW sw = {0};
  715. sw.skinType = SKINNEDWND_TYPE_DIALOG;
  716. sw.style = SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT;
  717. sw.hwndToSkin = hwndDlg;
  718. MLSkinWindow( plugin.hwndLibraryParent, &sw );
  719. /* skin status bar */
  720. sw.hwndToSkin = GetDlgItem( hwndDlg, IDC_PLSTATUS );
  721. sw.skinType = SKINNEDWND_TYPE_STATIC;
  722. sw.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS;
  723. MLSkinWindow( plugin.hwndLibraryParent, &sw );
  724. /* skin listview */
  725. HWND list = sw.hwndToSkin = GetDlgItem(hwndDlg, IDC_PLAYLIST_EDITOR);
  726. sw.skinType = SKINNEDWND_TYPE_LISTVIEW;
  727. sw.style = SWLVS_FULLROWSELECT | SWLVS_DOUBLEBUFFER | SWLVS_ALTERNATEITEMS | SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS;
  728. MLSkinWindow( plugin.hwndLibraryParent, &sw );
  729. MLSkinnedScrollWnd_ShowHorzBar( sw.hwndToSkin, FALSE );
  730. /* skin buttons */
  731. sw.skinType = SKINNEDWND_TYPE_BUTTON;
  732. sw.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | ( groupBtn ? SWBS_SPLITBUTTON : 0 );
  733. const int buttonidz[] = { IDC_PLAY, IDC_ENQUEUE, IDC_CUSTOM };
  734. for ( size_t i = 0; i != sizeof( buttonidz ) / sizeof( buttonidz[ 0 ] ); i++ )
  735. {
  736. sw.hwndToSkin = GetDlgItem( hwndDlg, buttonidz[ i ] );
  737. if ( IsWindow( sw.hwndToSkin ) )
  738. MLSkinWindow( plugin.hwndLibraryParent, &sw );
  739. }
  740. /* skin dropdown buttons */
  741. sw.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWBS_DROPDOWNBUTTON;
  742. const int buttonids[] = {IDC_BURN, IDC_ADD, IDC_REM, IDC_SEL, IDC_MISC, IDC_LIST};
  743. for ( size_t i = 0; i != sizeof( buttonids ) / sizeof( buttonids[ 0 ] ); i++ )
  744. {
  745. sw.hwndToSkin = GetDlgItem( hwndDlg, buttonids[ i ] );
  746. if ( IsWindow( sw.hwndToSkin ) )
  747. MLSkinWindow( plugin.hwndLibraryParent, &sw );
  748. }
  749. sw.style -= SWBS_DROPDOWNBUTTON;
  750. sw.hwndToSkin = GetDlgItem( hwndDlg, IDC_SAVE_PL );
  751. MLSkinWindow( plugin.hwndLibraryParent, &sw );
  752. // REVIEW: it'd be really nice to pass in a pointer to an ifc_playlist instead...
  753. // at this point, the main issue is how to delete/release it when we're done
  754. playlist_guid = tree_to_guid_map[ lParam ];
  755. { // scope for lock
  756. AutoLockT<api_playlists> lock( AGAVE_API_PLAYLISTS );
  757. PlaylistInfo info( playlist_guid );
  758. if ( info.Valid() )
  759. {
  760. // will check if the playlist file exists and update the view as needed
  761. const wchar_t *filename = info.GetFilename();
  762. if ( !PathFileExistsW( filename ) )
  763. {
  764. opened = false;
  765. RECT r = { 0 };
  766. HWND status = GetDlgItem( hwndDlg, IDC_PLSTATUS );
  767. GetWindowRect( hwndDlg, &r );
  768. MoveWindow( status, 20, 0, r.right - r.left - 40, r.bottom - r.top, FALSE );
  769. const int ids[] = { IDC_PLAYLIST_EDITOR, IDC_PLAY, IDC_BURN, IDC_ADD, IDC_REM, IDC_SEL, IDC_MISC, IDC_LIST, IDC_SAVE_PL };
  770. for ( size_t i = 0; i < sizeof( ids ) / sizeof( ids[ 0 ] ); i++ )
  771. ShowWindow( GetDlgItem( hwndDlg, ids[ i ] ), FALSE );
  772. // adjust the styles without needing extra resources, etc
  773. DWORD style = GetWindowLongPtr( status, GWL_STYLE );
  774. if ( style & SS_ENDELLIPSIS )
  775. style -= SS_ENDELLIPSIS;
  776. if ( style & SS_CENTERIMAGE )
  777. style -= SS_CENTERIMAGE;
  778. SetWindowLongPtr( status, GWL_STYLE, style | SS_CENTER | 0x2000 );
  779. wchar_t buf[ 1024 ] = { 0 };
  780. StringCchPrintfW( buf, 1024, WASABI_API_LNGSTRINGW( IDS_SOURCE_PL_MISSING ), filename );
  781. SetWindowTextW( status, buf );
  782. }
  783. lstrcpynW( currentPlaylistFilename, filename, MAX_PATH );
  784. playlist_Load( currentPlaylistFilename );
  785. lstrcpynW( currentPlaylistTitle, info.GetName(), FILETITLE_SIZE );
  786. }
  787. }
  788. SetPropW( hwndDlg, L"TITLE", currentPlaylistTitle );
  789. playlist_list.setwnd( list );
  790. playlist_list.AddCol( WASABI_API_LNGSTRINGW( IDS_TITLE ), 400 );
  791. int width = 27;
  792. MLCloudColumn_GetWidth( plugin.hwndLibraryParent, &width );
  793. playlist_list.AddCol( L"", ( cloud_avail ? width : 0 ) );
  794. playlist_list.AddAutoCol( WASABI_API_LNGSTRINGW( IDS_TIME ) );
  795. playlist_list.JustifyColumn( 2, LVCFMT_RIGHT );
  796. MLSkinnedHeader_SetCloudColumn( ListView_GetHeader( playlist_list.getwnd() ), ( cloud_avail ? 1 : -1 ) );
  797. if (!GetPropW(list, L"cloud_list_proc"))
  798. SetPropW( list, L"cloud_list_proc", (HANDLE)SetWindowLongPtrW( list, GWLP_WNDPROC, (LONG_PTR)playlist_cloud_listview ) );
  799. playlist_UpdateButtonText( hwndDlg, enqueuedef == 1 );
  800. SyncPlaylist();
  801. SetWindowRedraw( playlist_list.getwnd(), FALSE );
  802. }
  803. static void playlist_Paint( HWND hwndDlg )
  804. {
  805. int tab[] = { IDC_PLAYLIST_EDITOR | DCW_SUNKENBORDER };
  806. dialogSkinner.Draw( hwndDlg, tab, ( opened ? 1 : 0 ) );
  807. }
  808. static void AutoSizePlaylistColumns()
  809. {
  810. playlist_list.AutoSizeColumn( 2 );
  811. RECT channelRect;
  812. GetClientRect( playlist_list.getwnd(), &channelRect );
  813. ListView_SetColumnWidth( playlist_list.getwnd(), 0, channelRect.right - playlist_list.GetColumnWidth( 1 ) - playlist_list.GetColumnWidth( 2 ) );
  814. }
  815. enum
  816. {
  817. BPM_ECHO_WM_COMMAND = 0x1, // send WM_COMMAND and return value
  818. BPM_WM_COMMAND = 0x2, // just send WM_COMMAND
  819. };
  820. BOOL playlist_ButtonPopupMenu( HWND hwndDlg, int buttonId, HMENU menu, int flags = 0 )
  821. {
  822. RECT r;
  823. HWND buttonHWND = GetDlgItem( hwndDlg, buttonId );
  824. GetWindowRect( buttonHWND, &r );
  825. UpdateMenuItems( hwndDlg, menu );
  826. MLSkinnedButton_SetDropDownState( buttonHWND, TRUE );
  827. UINT tpmFlags = TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_BOTTOMALIGN | TPM_LEFTALIGN;
  828. if ( !( flags & BPM_WM_COMMAND ) )
  829. tpmFlags |= TPM_RETURNCMD;
  830. int x = Menu_TrackPopup( plugin.hwndLibraryParent, menu, tpmFlags, r.left, r.top, hwndDlg, NULL );
  831. if ( ( flags & BPM_ECHO_WM_COMMAND ) && x )
  832. SendMessage( hwndDlg, WM_COMMAND, MAKEWPARAM( x, 0 ), 0 );
  833. MLSkinnedButton_SetDropDownState( buttonHWND, FALSE );
  834. return x;
  835. }
  836. static void playlist_Burn( HWND hwndDlg )
  837. {
  838. HMENU blah = CreatePopupMenu();
  839. sendToIgnoreID = lastActiveID;
  840. sendTo.AddHere( hwndDlg, blah, ML_TYPE_FILENAMES );
  841. int x = playlist_ButtonPopupMenu( hwndDlg, IDC_BURN, blah );
  842. if ( sendTo.WasClicked( x ) )
  843. {
  844. int is_all = playlist_list.GetSelectedCount() == 0;
  845. wchar_t *names = BuildFilenameList( is_all );
  846. sendTo.SendFilenames( names );
  847. free( names );
  848. }
  849. sendTo.Cleanup();
  850. sendToIgnoreID = 0;
  851. }
  852. static void playlist_Play( HWND hwndDlg, HWND from, UINT idFrom )
  853. {
  854. HMENU listMenu = GetSubMenu( g_context_menus2, 0 );
  855. int count = GetMenuItemCount( listMenu );
  856. if ( count > 2 )
  857. {
  858. for ( int i = 2; i < count; i++ )
  859. DeleteMenu( listMenu, 2, MF_BYPOSITION );
  860. }
  861. playlist_ButtonPopupMenu( hwndDlg, idFrom, listMenu, BPM_WM_COMMAND );
  862. UpdatePlaylistTime( hwndDlg );
  863. }
  864. static void playlist_Sel( HWND hwndDlg, HWND from )
  865. {
  866. HMENU listMenu = GetSubMenu( GetSubMenu( g_context_menus, 3 ), 1 );
  867. UINT menuStatus;
  868. if ( playlist_list.GetNextSelected( -1 ) == -1 )
  869. menuStatus = MF_BYCOMMAND | MF_GRAYED;
  870. else
  871. menuStatus = MF_BYCOMMAND | MF_ENABLED;
  872. EnableMenuItem( listMenu, IDC_PLAYLIST_INVERT_SELECTION, menuStatus );
  873. if ( playlist_list.GetCount() > 0 )
  874. menuStatus = MF_BYCOMMAND | MF_ENABLED;
  875. else
  876. menuStatus = MF_BYCOMMAND | MF_GRAYED;
  877. EnableMenuItem( listMenu, IDC_PLAYLIST_SELECT_ALL, menuStatus );
  878. playlist_ButtonPopupMenu( hwndDlg, IDC_SEL, listMenu, BPM_WM_COMMAND );
  879. UpdatePlaylistTime( hwndDlg );
  880. }
  881. static void playlist_Rem( HWND hwndDlg, HWND from )
  882. {
  883. HMENU listMenu = GetSubMenu( GetSubMenu( g_context_menus, 3 ), 2 );
  884. UINT menuStatus;
  885. if ( playlist_list.GetNextSelected( -1 ) == -1 )
  886. menuStatus = MF_BYCOMMAND | MF_GRAYED;
  887. else
  888. menuStatus = MF_BYCOMMAND | MF_ENABLED;
  889. EnableMenuItem( listMenu, IDC_DELETE, menuStatus );
  890. EnableMenuItem( listMenu, IDC_CROP, menuStatus );
  891. if ( playlist_list.GetCount() > 0 )
  892. menuStatus = MF_BYCOMMAND | MF_ENABLED;
  893. else
  894. menuStatus = MF_BYCOMMAND | MF_GRAYED;
  895. EnableMenuItem( listMenu, IDC_PLAYLIST_REMOVE_DEAD, menuStatus );
  896. EnableMenuItem( listMenu, IDC_PLAYLIST_REMOVE_ALL, menuStatus );
  897. playlist_ButtonPopupMenu( hwndDlg, IDC_REM, listMenu, BPM_WM_COMMAND );
  898. UpdatePlaylistTime( hwndDlg );
  899. }
  900. static void playlist_Add( HWND hwndDlg, HWND from )
  901. {
  902. HMENU listMenu = GetSubMenu( GetSubMenu( g_context_menus, 3 ), 3 );
  903. playlist_ButtonPopupMenu( hwndDlg, IDC_ADD, listMenu, BPM_WM_COMMAND );
  904. UpdatePlaylistTime( hwndDlg );
  905. }
  906. static void playlist_Misc( HWND hwndDlg, HWND from )
  907. {
  908. HMENU listMenu = GetSubMenu( GetSubMenu( g_context_menus, 3 ), 4 );
  909. UINT menuStatus;
  910. if ( playlist_list.GetCount() > 0 )
  911. menuStatus = MF_BYCOMMAND | MF_ENABLED;
  912. else
  913. menuStatus = MF_BYCOMMAND | MF_GRAYED;
  914. EnableMenuItem( listMenu, IDC_PLAYLIST_RANDOMIZE, menuStatus );
  915. EnableMenuItem( listMenu, IDC_PLAYLIST_REVERSE, menuStatus );
  916. EnableMenuItem( listMenu, IDC_PLAYLIST_SORT_PATH, menuStatus );
  917. EnableMenuItem( listMenu, IDC_PLAYLIST_SORT_FILENAME, menuStatus );
  918. EnableMenuItem( listMenu, IDC_PLAYLIST_SORT_TITLE, menuStatus );
  919. EnableMenuItem( listMenu, IDC_PLAYLIST_RESET_CACHE, menuStatus );
  920. playlist_ButtonPopupMenu( hwndDlg, IDC_MISC, listMenu, BPM_WM_COMMAND );
  921. UpdatePlaylistTime( hwndDlg );
  922. }
  923. static void playlist_List( HWND hwndDlg, HWND from )
  924. {
  925. sendToIgnoreID = lastActiveID;
  926. HMENU listMenu = GetSubMenu( GetSubMenu( g_context_menus, 3 ), 5 );
  927. sendTo.AddHere( hwndDlg, GetSubMenu( listMenu, 1 ), ML_TYPE_FILENAMES );
  928. int x = playlist_ButtonPopupMenu( hwndDlg, IDC_LIST, listMenu, BPM_ECHO_WM_COMMAND );
  929. if ( sendTo.WasClicked( x ) )
  930. {
  931. wchar_t *names = BuildFilenameList( 1 );
  932. sendTo.SendFilenames( names );
  933. free( names );
  934. }
  935. sendTo.Cleanup();
  936. UpdatePlaylistTime( hwndDlg );
  937. sendToIgnoreID = 0;
  938. }
  939. static void playlist_Command( HWND hwndDlg, WPARAM wParam, LPARAM lParam )
  940. {
  941. switch ( LOWORD( wParam ) )
  942. {
  943. case IDC_BURN:
  944. playlist_Burn( hwndDlg );
  945. break;
  946. case IDC_PLAY:
  947. case IDC_ENQUEUE:
  948. {
  949. if ( HIWORD( wParam ) == MLBN_DROPDOWN )
  950. {
  951. playlist_Play( hwndDlg, (HWND)lParam, LOWORD( wParam ) );
  952. break;
  953. }
  954. else
  955. {
  956. // if it'l_enqueue_file from an accelerator, use the appropriate setting
  957. int action;
  958. if ( LOWORD( wParam ) == IDC_PLAY )
  959. action = ( HIWORD( wParam ) == 1 ) ? g_config->ReadInt( L"enqueuedef", 0 ) == 1 : 0;
  960. else
  961. action = ( HIWORD( wParam ) == 1 ) ? g_config->ReadInt( L"enqueuedef", 0 ) != 1 : 1;
  962. PlaySelection( action, playlist_list.GetSelectedCount() == 0 );
  963. }
  964. }
  965. break;
  966. case IDC_SEL:
  967. playlist_Sel( hwndDlg, (HWND)lParam );
  968. break;
  969. case IDC_REM:
  970. playlist_Rem( hwndDlg, (HWND)lParam );
  971. break;
  972. case IDC_ADD:
  973. playlist_Add( hwndDlg, (HWND)lParam );
  974. break;
  975. case IDC_MISC:
  976. playlist_Misc( hwndDlg, (HWND)lParam );
  977. break;
  978. case IDC_LIST:
  979. playlist_List( hwndDlg, (HWND)lParam );
  980. break;
  981. case IDC_DELETE:
  982. Playlist_DeleteSelected( 1 );
  983. break;
  984. case IDC_CROP:
  985. Playlist_DeleteSelected( 0 );
  986. break;
  987. case IDC_PLAYLIST_EXPLOREITEMFOLDER:
  988. Playlist_FindSelected();
  989. break;
  990. case IDC_PLAYLIST_VIEW_FILE_INFO:
  991. TagEditor( hwndDlg );
  992. break;
  993. case IDC_PLAYLIST_EDIT_ENTRY:
  994. EditEntry( hwndDlg );
  995. break;
  996. case IDC_PLAYLIST_DOWNLOAD_ENTRY:
  997. DownloadSelectedEntries( hwndDlg );
  998. break;
  999. case IDC_PLAYLIST_RANDOMIZE:
  1000. AGAVE_API_PLAYLISTMANAGER->Randomize( &currentPlaylist );
  1001. playlist_list.RefreshAll();
  1002. Changed();
  1003. break;
  1004. case IDC_PLAYLIST_REVERSE:
  1005. AGAVE_API_PLAYLISTMANAGER->Reverse( &currentPlaylist );
  1006. playlist_list.RefreshAll();
  1007. Changed();
  1008. break;
  1009. case IDC_PLAYLIST_RESET_CACHE:
  1010. Playlist_ResetSelected();
  1011. break;
  1012. case IDC_PLAYLIST_INVERT_SELECTION:
  1013. playlist_list.InvertSelection();
  1014. break;
  1015. case IDC_PLAYLIST_SELECT_ALL:
  1016. playlist_list.SelectAll();
  1017. break;
  1018. case IDC_ADD_FILES:
  1019. if ( CurrentPlaylist_AddFiles( hwndDlg ) ) Changed();
  1020. SyncPlaylist();
  1021. break;
  1022. case IDC_ADD_DIRECTORY:
  1023. if ( CurrentPlaylist_AddDirectory( hwndDlg ) ) Changed();
  1024. SyncPlaylist();
  1025. break;
  1026. case IDC_ADD_LOCATION:
  1027. if ( CurrentPlaylist_AddLocation( hwndDlg ) ) Changed();
  1028. SyncPlaylist();
  1029. break;
  1030. case IDC_PLAYLIST_SELECT_NONE:
  1031. playlist_list.UnselectAll();
  1032. break;
  1033. case IDC_PLAYLIST_REMOVE_DEAD:
  1034. if ( CurrentPlaylist_DeleteMissing() ) Changed();
  1035. SyncPlaylist();
  1036. break;
  1037. case IDC_PLAYLIST_REMOVE_ALL:
  1038. currentPlaylist.Clear();
  1039. Changed();
  1040. SyncPlaylist();
  1041. break;
  1042. case IDC_PLAYLIST_RECYCLE_SELECTED:
  1043. Playlist_RecycleSelected( hwndDlg, 1 );
  1044. break;
  1045. case IDC_PLAYLIST_SORT_PATH:
  1046. currentPlaylist.SortByDirectory();
  1047. Changed();
  1048. playlist_list.RefreshAll();
  1049. break;
  1050. case IDC_PLAYLIST_SORT_FILENAME:
  1051. currentPlaylist.SortByFilename();
  1052. Changed();
  1053. playlist_list.RefreshAll();
  1054. break;
  1055. case IDC_PLAYLIST_SORT_TITLE:
  1056. currentPlaylist.SortByTitle();
  1057. Changed();
  1058. playlist_list.RefreshAll();
  1059. break;
  1060. case IDC_EXPORT_PLAYLIST:
  1061. CurrentPlaylist_Export(hwndDlg);
  1062. break;
  1063. case IDC_IMPORT_PLAYLIST_FROM_FILE:
  1064. if ( currentPlaylist_ImportFromDisk( hwndDlg ) )
  1065. Changed();
  1066. SyncPlaylist();
  1067. break;
  1068. case IDC_IMPORT_WINAMP_PLAYLIST:
  1069. if ( currentPlaylist_ImportFromWinamp( hwndDlg ) ) Changed();
  1070. SyncPlaylist();
  1071. break;
  1072. case ID_PLAYLIST_GENERATE_HTML:
  1073. Playlist_GenerateHtmlPlaylist();
  1074. break;
  1075. case IDC_SAVE_PL:
  1076. if ( changed )
  1077. {
  1078. playlist_Save( hwndDlg );
  1079. Changed( false );
  1080. }
  1081. break;
  1082. }
  1083. }
  1084. void playlist_Save( HWND hwndDlg )
  1085. {
  1086. if ( opened )
  1087. {
  1088. if ( currentPlaylistFilename[ 0 ] )
  1089. {
  1090. if ( AGAVE_API_PLAYLISTMANAGER->Save( currentPlaylistFilename, &currentPlaylist ) == PLAYLISTMANAGER_FAILED )
  1091. {
  1092. wchar_t msg[ 512 ] = { 0 };
  1093. MessageBox( hwndDlg, WASABI_API_LNGSTRINGW_BUF( IDS_PLAYLIST_ERROR, msg, 512 ), WASABI_API_LNGSTRINGW( IDS_PLAYLIST_ERROR_TITLE ), MB_OK | MB_ICONWARNING );
  1094. }
  1095. }
  1096. PlaylistInfo info( playlist_guid );
  1097. info.SetSize( currentPlaylist.GetNumItems() );
  1098. info.SetLength( GetTotalLength() );
  1099. info.IssueSaveCallback();
  1100. }
  1101. }
  1102. void playlist_SaveGUID( GUID _guid )
  1103. {
  1104. if ( playlist_guid == _guid )
  1105. {
  1106. if ( currentPlaylistFilename[ 0 ] )
  1107. AGAVE_API_PLAYLISTMANAGER->Save( currentPlaylistFilename, &currentPlaylist );
  1108. PlaylistInfo info( playlist_guid );
  1109. info.SetSize( currentPlaylist.GetNumItems() );
  1110. info.SetLength( GetTotalLength() );
  1111. info.IssueSaveCallback();
  1112. }
  1113. }
  1114. void playlist_Destroy( HWND hwndDlg )
  1115. {
  1116. WASABI_API_APP->app_removeAccelerators( hwndDlg );
  1117. if ( changed )
  1118. playlist_Save( hwndDlg );
  1119. current_playing[ 0 ] = 0;
  1120. currentPlaylistFilename[ 0 ] = 0;
  1121. currentPlaylist.Clear();
  1122. playlist_list.setwnd( NULL );
  1123. RemovePropW( hwndDlg, L"TITLE" );
  1124. opened = false;
  1125. activeHWND = 0;
  1126. }
  1127. void SwapPlayEnqueueInMenu( HMENU listMenu )
  1128. {
  1129. int playPos = -1, enqueuePos = -1;
  1130. MENUITEMINFOW playItem = { sizeof( MENUITEMINFOW ), 0, }, enqueueItem = { sizeof( MENUITEMINFOW ), 0, };
  1131. int numItems = GetMenuItemCount( listMenu );
  1132. for ( int i = 0; i < numItems; i++ )
  1133. {
  1134. UINT id = GetMenuItemID( listMenu, i );
  1135. if ( id == IDC_PLAY )
  1136. {
  1137. playItem.fMask = MIIM_ID;
  1138. playPos = i;
  1139. GetMenuItemInfoW( listMenu, i, TRUE, &playItem );
  1140. }
  1141. else if ( id == IDC_ENQUEUE )
  1142. {
  1143. enqueueItem.fMask = MIIM_ID;
  1144. enqueuePos = i;
  1145. GetMenuItemInfoW( listMenu, i, TRUE, &enqueueItem );
  1146. }
  1147. }
  1148. playItem.wID = IDC_ENQUEUE;
  1149. enqueueItem.wID = IDC_PLAY;
  1150. SetMenuItemInfoW( listMenu, playPos, TRUE, &playItem );
  1151. SetMenuItemInfoW( listMenu, enqueuePos, TRUE, &enqueueItem );
  1152. }
  1153. void playlist_ContextMenu( HWND hwndDlg, HWND from, int x, int y )
  1154. {
  1155. if ( from != playlist_list.getwnd() )
  1156. return;
  1157. POINT pt = { x,y };
  1158. if ( x == -1 || y == -1 ) // x and y are -1 if the user invoked a shift-f10 popup menu
  1159. {
  1160. RECT channelRect = { 0 };
  1161. int selected = playlist_list.GetNextSelected();
  1162. if ( selected != -1 ) // if something is selected we'll drop the menu from there
  1163. {
  1164. playlist_list.GetItemRect( selected, &channelRect );
  1165. ClientToScreen( hwndDlg, (POINT *)&channelRect );
  1166. }
  1167. else // otherwise we'll drop it from the top-left corner of the listview, adjusting for the header location
  1168. {
  1169. GetWindowRect( hwndDlg, &channelRect );
  1170. HWND hHeader = (HWND)SNDMSG( from, LVM_GETHEADER, 0, 0L );
  1171. RECT headerRect;
  1172. if ( ( WS_VISIBLE & GetWindowLongPtr( hHeader, GWL_STYLE ) ) && GetWindowRect( hHeader, &headerRect ) )
  1173. channelRect.top += ( headerRect.bottom - headerRect.top );
  1174. }
  1175. x = channelRect.left;
  1176. y = channelRect.top;
  1177. }
  1178. HWND hHeader = (HWND)SNDMSG(from, LVM_GETHEADER, 0, 0L);
  1179. RECT headerRect;
  1180. if ( 0 == ( WS_VISIBLE & GetWindowLongPtr( hHeader, GWL_STYLE ) ) || FALSE == GetWindowRect( hHeader, &headerRect ) )
  1181. SetRectEmpty( &headerRect );
  1182. if ( FALSE != PtInRect( &headerRect, pt ) )
  1183. return;
  1184. sendToIgnoreID = lastActiveID;
  1185. HMENU listMenu = GetSubMenu( GetSubMenu( g_context_menus, 3 ), 0 );
  1186. menufucker_t mf = { sizeof( mf ),MENU_MLPLAYLIST,listMenu,0x3000,0x4000,0 };
  1187. UINT menuStatus, do_mf = 0;
  1188. if ( playlist_list.GetNextSelected( -1 ) == -1 )
  1189. {
  1190. menuStatus = MF_BYCOMMAND | MF_GRAYED;
  1191. EnableMenuItem( listMenu, 2, MF_BYPOSITION | MF_GRAYED );
  1192. }
  1193. else
  1194. {
  1195. menuStatus = MF_BYCOMMAND | MF_ENABLED;
  1196. EnableMenuItem( listMenu, 2, MF_BYPOSITION | MF_ENABLED );
  1197. sendTo.AddHere( hwndDlg, GetSubMenu( listMenu, 2 ), ML_TYPE_FILENAMES, 1 );
  1198. mf.extinf.mlplaylist.pl = &currentPlaylist;
  1199. mf.extinf.mlplaylist.list = playlist_list.getwnd();
  1200. pluginMessage message_build = { SendMessage( plugin.hwndWinampParent, WM_WA_IPC, ( WPARAM ) & "menufucker_build", IPC_REGISTER_WINAMP_IPCMESSAGE ),(intptr_t)&mf,0 };
  1201. SendMessage( plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&message_build, ML_IPC_SEND_PLUGIN_MESSAGE );
  1202. do_mf = 1;
  1203. }
  1204. EnableMenuItem( listMenu, IDC_PLAYLIST_EXPLOREITEMFOLDER, menuStatus );
  1205. EnableMenuItem( listMenu, IDC_PLAYLIST_VIEW_FILE_INFO, menuStatus );
  1206. EnableMenuItem( listMenu, IDC_PLAYLIST_EDIT_ENTRY, menuStatus );
  1207. int l_mark = playlist_list.GetSelectionMark();
  1208. if ( l_mark != -1 && !currentPlaylist.entries[ l_mark ]->isLocal() )
  1209. EnableMenuItem( listMenu, IDC_PLAYLIST_DOWNLOAD_ENTRY, menuStatus );
  1210. else
  1211. EnableMenuItem( listMenu, IDC_PLAYLIST_DOWNLOAD_ENTRY, MF_BYCOMMAND | MF_GRAYED );
  1212. EnableMenuItem( listMenu, IDC_DELETE, menuStatus );
  1213. EnableMenuItem( listMenu, IDC_CROP, menuStatus );
  1214. HMENU cloud_hmenu = 0;
  1215. if ( playlists_CloudAvailable() )
  1216. {
  1217. int mark = playlist_list.GetSelectionMark();
  1218. if ( mark != -1 )
  1219. {
  1220. wchar_t filename[ 1024 ] = { 0 };
  1221. currentPlaylist.entries[ mark ]->GetFilename( filename, 1024 );
  1222. cloud_hmenu = CreatePopupMenu();
  1223. WASABI_API_SYSCB->syscb_issueCallback( api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_GET_CLOUD_STATUS, (intptr_t)&filename, (intptr_t)&cloud_hmenu );
  1224. if ( cloud_hmenu )
  1225. {
  1226. MENUITEMINFOW m = { sizeof( m ), MIIM_TYPE | MIIM_ID | MIIM_SUBMENU, MFT_SEPARATOR, 0 };
  1227. m.wID = CLOUD_SOURCE_MENUS - 1;
  1228. InsertMenuItemW( listMenu, 3, TRUE, &m );
  1229. wchar_t a[ 100 ] = { 0 };
  1230. m.fType = MFT_STRING;
  1231. m.dwTypeData = WASABI_API_LNGSTRINGW_BUF( IDS_CLOUD_SOURCES, a, 100 );
  1232. m.wID = CLOUD_SOURCE_MENUS;
  1233. m.hSubMenu = cloud_hmenu;
  1234. InsertMenuItemW( listMenu, 4, TRUE, &m );
  1235. }
  1236. }
  1237. }
  1238. UpdateMenuItems(hwndDlg, listMenu);
  1239. int r = Menu_TrackPopup( plugin.hwndLibraryParent, listMenu, TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_LEFTALIGN | TPM_RETURNCMD, x, y, hwndDlg, NULL );
  1240. if ( r )
  1241. SendMessage( hwndDlg, WM_COMMAND, MAKEWPARAM( r, 0 ), 0 );
  1242. if ( do_mf )
  1243. {
  1244. pluginMessage message_result = { SendMessage( plugin.hwndWinampParent, WM_WA_IPC, ( WPARAM ) & "menufucker_result", IPC_REGISTER_WINAMP_IPCMESSAGE ), (intptr_t)&mf, r, 0 };
  1245. SendMessage( plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&message_result, ML_IPC_SEND_PLUGIN_MESSAGE );
  1246. }
  1247. switch ( r )
  1248. {
  1249. case 0:
  1250. break;
  1251. case IDC_PLAYLIST_EXPLOREITEMFOLDER:
  1252. case IDC_PLAYLIST_VIEW_FILE_INFO:
  1253. case IDC_PLAYLIST_EDIT_ENTRY:
  1254. SendMessage( hwndDlg, WM_NEXTDLGCTL, (WPARAM)from, (LPARAM)TRUE );
  1255. break;
  1256. case IDC_PLAYLIST_DOWNLOAD_ENTRY:
  1257. break;
  1258. default:
  1259. if ( !( menuStatus & MF_GRAYED ) && sendTo.WasClicked( r ) )
  1260. {
  1261. wchar_t *names = BuildFilenameList( 0 );
  1262. sendTo.SendFilenames( names );
  1263. free( names );
  1264. }
  1265. else
  1266. {
  1267. if ( r >= CLOUD_SOURCE_MENUS && r < CLOUD_SOURCE_MENUS_PL_UPPER ) // deals with cloud specific menus
  1268. {
  1269. // 0 = no change
  1270. // 1 = adding to cloud
  1271. // 2 = added locally
  1272. // 4 = removed
  1273. int mode = 0; // deals with cloud specific menus
  1274. WASABI_API_SYSCB->syscb_issueCallback( api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_PROCESS_CLOUD_STATUS, (intptr_t)r, (intptr_t)&mode );
  1275. // TODO
  1276. /*switch (mode)
  1277. {
  1278. case 1:
  1279. setCloudValue(&itemCache.Items[pnmitem->iItem], L"5");
  1280. break;
  1281. case 2:
  1282. setCloudValue(&itemCache.Items[pnmitem->iItem], L"4");
  1283. break;
  1284. case 4:
  1285. setCloudValue(&itemCache.Items[pnmitem->iItem], L"4");
  1286. break;
  1287. }
  1288. InvalidateRect(resultlist.getwnd(), NULL, TRUE);*/
  1289. }
  1290. }
  1291. break;
  1292. }
  1293. if (!(menuStatus & MF_GRAYED))
  1294. sendTo.Cleanup();
  1295. sendToIgnoreID = 0;
  1296. if ( cloud_hmenu )
  1297. {
  1298. DeleteMenu( listMenu, CLOUD_SOURCE_MENUS - 1, MF_BYCOMMAND );
  1299. DeleteMenu( listMenu, CLOUD_SOURCE_MENUS, MF_BYCOMMAND );
  1300. DestroyMenu( cloud_hmenu );
  1301. }
  1302. }
  1303. static void playlist_LeftButtonUp( HWND hwndDlg, WPARAM wParam, POINTS pts )
  1304. {
  1305. if ( SCROLLDIR_NONE != scrollDirection )
  1306. {
  1307. KillTimer( hwndDlg, SCROLLTIMER_ID );
  1308. scrollDirection = SCROLLDIR_NONE;
  1309. }
  1310. if ( we_are_drag_and_dropping && GetCapture() == hwndDlg )
  1311. {
  1312. ReleaseCapture();
  1313. BOOL handled = FALSE;
  1314. POINT pt;
  1315. POINTSTOPOINT( pt, pts );
  1316. MapWindowPoints( hwndDlg, HWND_DESKTOP, &pt, 1 );
  1317. HWND hTarget = WindowFromPoint( pt );
  1318. if ( hTarget == playlist_list.getwnd() )
  1319. {
  1320. LVHITTESTINFO hitTest = { 0 };
  1321. POINTSTOPOINT( hitTest.pt, pts );
  1322. MapWindowPoints( hwndDlg, playlist_list.getwnd(), &hitTest.pt, 1 );
  1323. ListView_HitTest( playlist_list.getwnd(), &hitTest );
  1324. size_t position = hitTest.iItem;
  1325. if ( ( hitTest.flags & ( LVHT_ONITEM ) ) );
  1326. else if ( hitTest.flags & LVHT_ABOVE )
  1327. position = 0;
  1328. else if ( hitTest.flags & ( LVHT_BELOW | LVHT_NOWHERE ) )
  1329. position = playlist_list.GetCount();
  1330. if ( position != -1 )
  1331. {
  1332. RECT itemRect;
  1333. playlist_list.GetItemRect( position, &itemRect );
  1334. if ( hitTest.pt.y > ( itemRect.bottom + ( itemRect.top - itemRect.bottom ) / 2 ) )
  1335. position++;
  1336. Playlist tempList;
  1337. size_t selected = -1, numDeleted = 0;
  1338. // first, make a temporary list with all the selected items
  1339. // being careful to deal with the discrepancy between the listview and the real playlist
  1340. // as we remove items
  1341. while ( ( selected = playlist_list.GetNextSelected( selected ) ) != -1 )
  1342. {
  1343. tempList.entries.push_back(currentPlaylist.entries.at(selected - numDeleted));
  1344. currentPlaylist.entries.erase(currentPlaylist.entries.begin() + (selected - numDeleted));
  1345. if ((selected - numDeleted) < position)
  1346. position--;
  1347. numDeleted++;
  1348. }
  1349. playlist_list.UnselectAll();
  1350. // if dragging to the end of the playlist, handle things a bit differently from normal
  1351. if ( position > currentPlaylist.entries.size() )
  1352. {
  1353. position--;
  1354. while ( numDeleted-- )
  1355. {
  1356. currentPlaylist.entries.insert(currentPlaylist.entries.end(), tempList.entries.at(0));
  1357. playlist_list.SetSelected(position++); // we want the same filenames to be selected
  1358. tempList.entries.erase(tempList.entries.begin());
  1359. }
  1360. }
  1361. else
  1362. {
  1363. while ( numDeleted-- )
  1364. {
  1365. playlist_list.SetSelected(position); // we want the same filenames to be selected
  1366. currentPlaylist.entries.insert(currentPlaylist.entries.begin() + position, tempList.entries.at(0));
  1367. position++;
  1368. tempList.entries.erase(tempList.entries.begin());
  1369. }
  1370. }
  1371. Changed();
  1372. SyncPlaylist();
  1373. handled = TRUE;
  1374. }
  1375. }
  1376. we_are_drag_and_dropping = 0;
  1377. if ( !handled )
  1378. {
  1379. mlDropItemStruct m = { 0 };
  1380. m.type = ML_TYPE_FILENAMESW;
  1381. m.p = pt;
  1382. pluginHandleIpcMessage( ML_IPC_HANDLEDRAG, (WPARAM)&m );
  1383. if ( m.result > 0 )
  1384. {
  1385. wchar_t *names = BuildFilenameList( 0 );
  1386. m.flags = 0;
  1387. m.result = 0;
  1388. m.data = (void *)names;
  1389. pluginHandleIpcMessage( ML_IPC_HANDLEDROP, (WPARAM)&m );
  1390. free( names );
  1391. }
  1392. }
  1393. }
  1394. }
  1395. static void CALLBACK playlist_OnScrollTimer( HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime )
  1396. {
  1397. HWND hList = GetDlgItem( hwnd, IDC_PLAYLIST_EDITOR );
  1398. if ( SCROLLDIR_NONE == scrollDirection || NULL == hList )
  1399. {
  1400. KillTimer( hwnd, idEvent );
  1401. return;
  1402. }
  1403. RECT rc;
  1404. rc.left = LVIR_BOUNDS;
  1405. if ( SendMessage( hList, LVM_GETITEMRECT, (WPARAM)0, (LPARAM)&rc ) )
  1406. {
  1407. INT height = rc.bottom - rc.top;
  1408. if ( SCROLLDIR_UP == scrollDirection )
  1409. height = -height;
  1410. SendMessage( hList, LVM_SCROLL, 0, (LPARAM)height );
  1411. }
  1412. if ( scrollTimerElapse == scrollDelay )
  1413. {
  1414. static INT scrollInterval = 0;
  1415. if ( 0 == scrollInterval )
  1416. scrollInterval = GetProfileInt( TEXT( "windows" ), TEXT( "DragScrollInterval" ), DD_DEFSCROLLINTERVAL );
  1417. if ( 0 != scrollInterval )
  1418. SetTimer( hwnd, idEvent, scrollTimerElapse, playlist_OnScrollTimer );
  1419. else
  1420. KillTimer( hwnd, idEvent );
  1421. }
  1422. }
  1423. static INT playlist_GetScrollDirection( HWND hList, POINT pt )
  1424. {
  1425. static INT scrollZone = 0;
  1426. if ( 0 == scrollZone )
  1427. scrollZone = GetProfileInt( TEXT( "windows" ), TEXT( "DragScrollInset" ), DD_DEFSCROLLINSET );
  1428. RECT rc, rcTest;
  1429. if ( 0 == scrollZone || !GetClientRect( playlist_list.getwnd(), &rc ) )
  1430. return SCROLLDIR_NONE;
  1431. CopyRect( &rcTest, &rc );
  1432. rcTest.top = rcTest.bottom - scrollZone;
  1433. if ( PtInRect( &rcTest, pt ) )
  1434. return SCROLLDIR_DOWN;
  1435. rcTest.top = rc.top;
  1436. rcTest.bottom = rcTest.top + scrollZone;
  1437. if ( 0 == ( LVS_NOCOLUMNHEADER & GetWindowLongPtr( hList, GWL_STYLE ) ) )
  1438. {
  1439. HWND hHeader = (HWND)SendMessage( hList, LVM_GETHEADER, 0, 0L );
  1440. if ( NULL != hHeader && 0 != ( WS_VISIBLE & GetWindowLongPtr( hHeader, GWL_STYLE ) ) )
  1441. {
  1442. RECT rcHeader;
  1443. if ( GetWindowRect( hHeader, &rcHeader ) )
  1444. {
  1445. MapWindowPoints( HWND_DESKTOP, hList, ( (POINT *)&rcHeader ) + 1, 1 );
  1446. INT offset = rcHeader.bottom - rc.top;
  1447. if ( 0 != offset )
  1448. OffsetRect( &rcTest, 0, offset );
  1449. }
  1450. }
  1451. }
  1452. if ( PtInRect( &rcTest, pt ) )
  1453. return SCROLLDIR_UP;
  1454. return SCROLLDIR_NONE;
  1455. }
  1456. static void playlist_MouseMove( HWND hwndDlg, POINTS pts )
  1457. {
  1458. if ( we_are_drag_and_dropping && GetCapture() == hwndDlg )
  1459. {
  1460. BOOL handled = FALSE;
  1461. POINT pt;
  1462. POINTSTOPOINT( pt, pts );
  1463. MapWindowPoints( hwndDlg, HWND_DESKTOP, &pt, 1 );
  1464. HWND hTarget = WindowFromPoint( pt );
  1465. INT scroll = SCROLLDIR_NONE;
  1466. if ( hTarget == playlist_list.getwnd() )
  1467. {
  1468. LVHITTESTINFO hitTest = { 0 };
  1469. POINTSTOPOINT( hitTest.pt, pts );
  1470. MapWindowPoints( hwndDlg, playlist_list.getwnd(), &hitTest.pt, 1 );
  1471. int position = ListView_HitTest( playlist_list.getwnd(), &hitTest );
  1472. if ( position != -1 )
  1473. {
  1474. scroll = playlist_GetScrollDirection( playlist_list.getwnd(), hitTest.pt );
  1475. handled = TRUE;
  1476. }
  1477. }
  1478. if ( scroll != scrollDirection )
  1479. {
  1480. if ( SCROLLDIR_NONE == scroll )
  1481. {
  1482. KillTimer( hwndDlg, SCROLLTIMER_ID );
  1483. }
  1484. else
  1485. {
  1486. if ( SCROLLDIR_NONE == scrollDirection )
  1487. {
  1488. if ( 0 == scrollDelay )
  1489. scrollDelay = GetProfileInt( TEXT( "windows" ), TEXT( "DragScrollDelay" ), DD_DEFSCROLLDELAY );
  1490. if ( 0 != scrollDelay )
  1491. {
  1492. scrollTimerElapse = scrollDelay;
  1493. SetTimer( hwndDlg, SCROLLTIMER_ID, scrollTimerElapse, playlist_OnScrollTimer );
  1494. }
  1495. }
  1496. }
  1497. scrollDirection = scroll;
  1498. }
  1499. if ( !handled )
  1500. {
  1501. mlDropItemStruct m = { 0 };
  1502. m.type = ML_TYPE_FILENAMES;
  1503. m.p = pt;
  1504. m.flags = 0; //ML_HANDLEDRAG_FLAG_NOCURSOR;
  1505. pluginHandleIpcMessage( ML_IPC_HANDLEDRAG, (WPARAM)&m );
  1506. }
  1507. else
  1508. SetCursor( hDragNDropCursor );
  1509. }
  1510. }
  1511. void playlist_Reload( bool forced )
  1512. {
  1513. if ( opened || forced )
  1514. {
  1515. if ( !opened && forced )
  1516. {
  1517. PostMessage( plugin.hwndLibraryParent, WM_USER + 30, 0, 0 );
  1518. return;
  1519. }
  1520. playlist_Load( currentPlaylistFilename );
  1521. // reverted back to known state so any
  1522. // of our current changes are now gone
  1523. Changed( false );
  1524. SyncPlaylist();
  1525. }
  1526. }
  1527. void playlist_ReloadGUID( GUID _guid )
  1528. {
  1529. if ( playlist_guid == _guid )
  1530. playlist_Reload( true );
  1531. }
  1532. void playlist_Unload( HWND hwndDlg )
  1533. {
  1534. currentPlaylist.Clear();
  1535. currentPlaylistFilename[ 0 ] = 0x0000;
  1536. ListView_SetItemCount( playlist_list.getwnd(), 0 );
  1537. ListView_RedrawItems( playlist_list.getwnd(), 0, 0 );
  1538. UpdatePlaylistTime( hwndDlg );
  1539. }
  1540. void playlist_DropFiles( HDROP hDrop )
  1541. {
  1542. wchar_t temp[ 2048 ] = { 0 };
  1543. int x;
  1544. int y = DragQueryFileW( hDrop, 0xffffffff, temp, 2048 );
  1545. Playlist newPlaylist;
  1546. for ( x = 0; x < y; x++ )
  1547. {
  1548. DragQueryFileW( hDrop, x, temp, 2048 );
  1549. if ( PathIsDirectory( temp ) )
  1550. {
  1551. PlaylistDirectoryCallback dirCallback( mediaLibrary.GetExtensionList() );
  1552. AGAVE_API_PLAYLISTMANAGER->LoadDirectory( temp, &newPlaylist, &dirCallback );
  1553. }
  1554. else
  1555. {
  1556. if ( AGAVE_API_PLAYLISTMANAGER->Load( temp, &newPlaylist ) != PLAYLISTMANAGER_SUCCESS )
  1557. {
  1558. //wchar_t title[400];
  1559. //int length;
  1560. //mediaLibrary.GetFileInfo(temp, title, 400, &length);
  1561. //newPlaylist.AppendWithInfo(temp, title, length*1000);
  1562. newPlaylist.AppendWithInfo( temp, 0, 0 ); // add with NULL info, will be fetched as needed
  1563. }
  1564. }
  1565. }
  1566. LVHITTESTINFO hitTest;
  1567. DragQueryPoint( hDrop, &hitTest.pt );
  1568. ListView_HitTest( playlist_list.getwnd(), &hitTest );
  1569. if ( hitTest.iItem != -1 )
  1570. {
  1571. RECT itemRect;
  1572. playlist_list.GetItemRect( hitTest.iItem, &itemRect );
  1573. if ( hitTest.pt.y > ( itemRect.bottom + ( itemRect.top - itemRect.bottom ) / 2 ) )
  1574. {
  1575. hitTest.iItem++;
  1576. if ( hitTest.iItem >= playlist_list.GetCount() )
  1577. hitTest.iItem = -1;
  1578. }
  1579. }
  1580. if ( hitTest.flags & LVHT_BELOW || hitTest.iItem == -1 )
  1581. currentPlaylist.AppendPlaylist( newPlaylist );
  1582. else
  1583. currentPlaylist.InsertPlaylist( newPlaylist, hitTest.iItem );
  1584. DragFinish( hDrop );
  1585. Changed();
  1586. SyncPlaylist();
  1587. }
  1588. static HRGN g_rgnUpdate = NULL;
  1589. static int offsetX = 0, offsetY = 0;
  1590. typedef struct _LAYOUT
  1591. {
  1592. INT id;
  1593. HWND hwnd;
  1594. INT x;
  1595. INT y;
  1596. INT cx;
  1597. INT cy;
  1598. DWORD flags;
  1599. HRGN rgn;
  1600. }
  1601. LAYOUT, PLAYOUT;
  1602. #define SETLAYOUTPOS(_layout, _x, _y, _cx, _cy) { _layout->x=_x; _layout->y=_y;_layout->cx=_cx;_layout->cy=_cy;_layout->rgn=NULL; }
  1603. #define SETLAYOUTFLAGS(_layout, _r) \
  1604. { \
  1605. BOOL fVis; \
  1606. fVis = (WS_VISIBLE & (LONG)GetWindowLongPtr(_layout->hwnd, GWL_STYLE)); \
  1607. if (_layout->x == _r.left && _layout->y == _r.top) _layout->flags |= SWP_NOMOVE; \
  1608. if (_layout->cx == (_r.right - _r.left) && _layout->cy == (_r.bottom - _r.top)) _layout->flags |= SWP_NOSIZE; \
  1609. if ((SWP_HIDEWINDOW & _layout->flags) && !fVis) _layout->flags &= ~SWP_HIDEWINDOW; \
  1610. if ((SWP_SHOWWINDOW & _layout->flags) && fVis) _layout->flags &= ~SWP_SHOWWINDOW; \
  1611. }
  1612. #define LAYOUTNEEEDUPDATE(_layout) ((SWP_NOMOVE | SWP_NOSIZE) != ((SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW | SWP_SHOWWINDOW) & _layout->flags))
  1613. #define GROUP_MIN 0x1
  1614. #define GROUP_MAX 0x2
  1615. #define GROUP_STATUSBAR 0x1
  1616. #define GROUP_MAIN 0x2
  1617. static void LayoutWindows( HWND hwnd, BOOL fRedraw, BOOL fUpdateAll = FALSE )
  1618. {
  1619. static INT controls[] =
  1620. {
  1621. GROUP_STATUSBAR, IDC_PLAY, IDC_ENQUEUE, IDC_CUSTOM, IDC_BURN, IDC_ADD, IDC_REM, IDC_SEL, IDC_MISC, IDC_LIST, IDC_SAVE_PL, IDC_PLSTATUS,
  1622. GROUP_MAIN, IDC_PLAYLIST_EDITOR
  1623. };
  1624. INT index;
  1625. RECT rc, rg, ri;
  1626. LAYOUT layout[ sizeof( controls ) / sizeof( controls[ 0 ] ) ], *pl;
  1627. BOOL skipgroup;
  1628. HRGN rgn = NULL;
  1629. GetClientRect( hwnd, &rc );
  1630. if ( rc.right == rc.left || rc.bottom == rc.top )
  1631. return;
  1632. if ( rc.right > WASABI_API_APP->getScaleX( 4 ) )
  1633. rc.right -= WASABI_API_APP->getScaleX( 4 );
  1634. SetRect( &rg, rc.left, rc.top, rc.right, rc.top );
  1635. pl = layout;
  1636. skipgroup = FALSE;
  1637. InvalidateRect( hwnd, NULL, TRUE );
  1638. for ( index = 0; index < sizeof( controls ) / sizeof( *controls ); index++ )
  1639. {
  1640. if ( controls[ index ] >= GROUP_MIN && controls[ index ] <= GROUP_MAX ) // group id
  1641. {
  1642. skipgroup = FALSE;
  1643. switch ( controls[ index ] )
  1644. {
  1645. case GROUP_STATUSBAR:
  1646. if ( opened )
  1647. {
  1648. wchar_t buffer[ 128 ] = { 0 };
  1649. GetDlgItemTextW( hwnd, IDC_PLAY, buffer, ARRAYSIZE( buffer ) );
  1650. LRESULT idealSize = MLSkinnedButton_GetIdealSize( GetDlgItem( hwnd, IDC_PLAY ), buffer );
  1651. SetRect( &rg, rc.left + WASABI_API_APP->getScaleX( 1 ), rc.bottom - WASABI_API_APP->getScaleY( HIWORD( idealSize ) ), rc.right, rc.bottom );
  1652. rc.bottom = rg.top - WASABI_API_APP->getScaleY( 3 );
  1653. }
  1654. else
  1655. {
  1656. SetRect( &rg, rc.left, rc.top, rc.right, rc.bottom );
  1657. }
  1658. break;
  1659. case GROUP_MAIN:
  1660. if ( opened )
  1661. SetRect( &rg, rc.left + WASABI_API_APP->getScaleX( 1 ), rc.top, rc.right, rc.bottom );
  1662. else
  1663. skipgroup = TRUE;
  1664. break;
  1665. }
  1666. continue;
  1667. }
  1668. if (skipgroup)
  1669. continue;
  1670. pl->id = controls[index];
  1671. pl->hwnd = GetDlgItem(hwnd, pl->id);
  1672. if ( !pl->hwnd )
  1673. continue;
  1674. GetWindowRect( pl->hwnd, &ri );
  1675. MapWindowPoints( HWND_DESKTOP, hwnd, (LPPOINT)&ri, 2 );
  1676. pl->flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS;
  1677. switch ( pl->id )
  1678. {
  1679. case IDC_PLAY:
  1680. case IDC_ENQUEUE:
  1681. case IDC_CUSTOM:
  1682. case IDC_BURN:
  1683. case IDC_ADD:
  1684. case IDC_SEL:
  1685. case IDC_REM:
  1686. case IDC_MISC:
  1687. case IDC_LIST:
  1688. case IDC_SAVE_PL:
  1689. if ( opened && ( IDC_CUSTOM != pl->id || customAllowed ) )
  1690. {
  1691. if ( groupBtn && pl->id == IDC_PLAY && enqueuedef == 1 )
  1692. {
  1693. pl->flags |= SWP_HIDEWINDOW;
  1694. break;
  1695. }
  1696. if ( groupBtn && pl->id == IDC_ENQUEUE && enqueuedef != 1 )
  1697. {
  1698. pl->flags |= SWP_HIDEWINDOW;
  1699. break;
  1700. }
  1701. wchar_t buffer[ 128 ] = { 0 };
  1702. GetWindowText( pl->hwnd, buffer, ARRAYSIZE( buffer ) );
  1703. LRESULT idealSize = MLSkinnedButton_GetIdealSize( pl->hwnd, buffer );
  1704. LONG width = LOWORD( idealSize ) + ( pl->id == IDC_PLAY || pl->id == IDC_ENQUEUE || pl->id == IDC_CUSTOM || pl->id == IDC_SAVE_PL ? WASABI_API_APP->getScaleX( 6 ) : 0 );
  1705. SETLAYOUTPOS( pl, rg.left, rg.bottom - WASABI_API_APP->getScaleY( HIWORD( idealSize ) ), width, WASABI_API_APP->getScaleY( HIWORD( idealSize ) ) );
  1706. pl->flags |= ( ( rg.right - rg.left ) > width ) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW;
  1707. if ( SWP_SHOWWINDOW & pl->flags )
  1708. rg.left += ( pl->cx + WASABI_API_APP->getScaleX( 4 ) );
  1709. }
  1710. else
  1711. {
  1712. pl->flags |= SWP_HIDEWINDOW;
  1713. }
  1714. break;
  1715. case IDC_PLSTATUS:
  1716. if ( opened )
  1717. {
  1718. wchar_t buffer[ 128 ] = { 0 };
  1719. GetWindowTextW( pl->hwnd, buffer, ARRAYSIZE( buffer ) );
  1720. LRESULT idealSize = MLSkinnedStatic_GetIdealSize( pl->hwnd, buffer );
  1721. SETLAYOUTPOS( pl, rg.left, rg.bottom - WASABI_API_APP->getScaleY( HIWORD( idealSize ) ), rg.right - rg.left, WASABI_API_APP->getScaleY( HIWORD( idealSize ) ) );
  1722. if ( SWP_SHOWWINDOW & pl->flags )
  1723. rg.top = ( pl->y + pl->cy + WASABI_API_APP->getScaleY( 1 ) );
  1724. }
  1725. else
  1726. {
  1727. SETLAYOUTPOS( pl, rg.left + WASABI_API_APP->getScaleY( 20 ),
  1728. rg.top + WASABI_API_APP->getScaleY( 1 ),
  1729. rg.right - rg.left - WASABI_API_APP->getScaleY( 40 ),
  1730. rg.bottom - WASABI_API_APP->getScaleY( 2 ) );
  1731. }
  1732. break;
  1733. case IDC_PLAYLIST_EDITOR:
  1734. if ( opened )
  1735. {
  1736. pl->flags |= ( rg.top < rg.bottom ) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW;
  1737. 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 ) );
  1738. }
  1739. else
  1740. {
  1741. pl->flags |= SWP_HIDEWINDOW;
  1742. }
  1743. break;
  1744. }
  1745. SETLAYOUTFLAGS( pl, ri );
  1746. if ( LAYOUTNEEEDUPDATE( pl ) )
  1747. {
  1748. if ( SWP_NOSIZE == ( ( SWP_HIDEWINDOW | SWP_SHOWWINDOW | SWP_NOSIZE ) & pl->flags ) && ri.left == ( pl->x + offsetX ) && ri.top == ( pl->y + offsetY ) && IsWindowVisible( pl->hwnd ) )
  1749. {
  1750. SetRect( &ri, pl->x, pl->y, pl->cx + pl->x, pl->y + pl->cy );
  1751. ValidateRect( hwnd, &ri );
  1752. }
  1753. pl++;
  1754. }
  1755. else if ( ( fRedraw || ( !offsetX && !offsetY ) ) && IsWindowVisible( pl->hwnd ) )
  1756. {
  1757. ValidateRect( hwnd, &ri );
  1758. if ( GetUpdateRect( pl->hwnd, NULL, FALSE ) )
  1759. {
  1760. if ( !rgn )
  1761. rgn = CreateRectRgn( 0, 0, 0, 0 );
  1762. GetUpdateRgn( pl->hwnd, rgn, FALSE );
  1763. OffsetRgn( rgn, pl->x, pl->y );
  1764. InvalidateRgn( hwnd, rgn, FALSE );
  1765. }
  1766. }
  1767. }
  1768. if ( pl != layout )
  1769. {
  1770. LAYOUT *pc;
  1771. HDWP hdwp = BeginDeferWindowPos( (INT)( pl - layout ) );
  1772. for ( pc = layout; pc < pl && hdwp; pc++ )
  1773. hdwp = DeferWindowPos( hdwp, pc->hwnd, NULL, pc->x, pc->y, pc->cx, pc->cy, pc->flags );
  1774. if ( hdwp )
  1775. EndDeferWindowPos( hdwp );
  1776. if ( !rgn )
  1777. rgn = CreateRectRgn( 0, 0, 0, 0 );
  1778. for ( pc = layout; pc < pl && hdwp; pc++ )
  1779. {
  1780. switch ( pc->id )
  1781. {
  1782. case IDC_PLAYLIST_EDITOR:
  1783. PostMessage( hwnd, WM_APP + 100, 0, 0 );
  1784. break;
  1785. }
  1786. }
  1787. if ( fRedraw )
  1788. {
  1789. GetUpdateRgn( hwnd, rgn, FALSE );
  1790. for ( pc = layout; pc < pl && hdwp; pc++ )
  1791. {
  1792. if ( pc->rgn )
  1793. {
  1794. OffsetRgn( pc->rgn, pc->x, pc->y );
  1795. CombineRgn( rgn, rgn, pc->rgn, RGN_OR );
  1796. }
  1797. }
  1798. RedrawWindow( hwnd, NULL, rgn, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASENOW | RDW_ALLCHILDREN );
  1799. }
  1800. if ( g_rgnUpdate )
  1801. {
  1802. GetUpdateRgn( hwnd, g_rgnUpdate, FALSE );
  1803. for ( pc = layout; pc < pl && hdwp; pc++ )
  1804. {
  1805. if ( pc->rgn )
  1806. {
  1807. OffsetRgn( pc->rgn, pc->x, pc->y );
  1808. CombineRgn( g_rgnUpdate, g_rgnUpdate, pc->rgn, RGN_OR );
  1809. }
  1810. }
  1811. }
  1812. for ( pc = layout; pc < pl && hdwp; pc++ )
  1813. {
  1814. if ( pc->rgn )
  1815. DeleteObject( pc->rgn );
  1816. }
  1817. }
  1818. if ( rgn )
  1819. DeleteObject( rgn );
  1820. ValidateRgn( hwnd, NULL );
  1821. }
  1822. INT_PTR CALLBACK view_playlistDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
  1823. {
  1824. INT_PTR a = dialogSkinner.Handle( hwndDlg, uMsg, wParam, lParam );
  1825. if ( a )
  1826. return a;
  1827. switch (uMsg)
  1828. {
  1829. case WM_INITMENUPOPUP:
  1830. sendTo.InitPopupMenu( wParam );
  1831. return 0;
  1832. case WM_MOUSEMOVE:
  1833. playlist_MouseMove( hwndDlg, MAKEPOINTS( lParam ) );
  1834. return 0;
  1835. case WM_LBUTTONUP:
  1836. playlist_LeftButtonUp( hwndDlg, wParam, MAKEPOINTS( lParam ) );
  1837. return 0;
  1838. case WM_PAINT:
  1839. playlist_Paint( hwndDlg );
  1840. return 0;
  1841. case WM_INITDIALOG:
  1842. playlist_Init( hwndDlg, lParam );
  1843. return TRUE;
  1844. case WM_DESTROY:
  1845. playlist_Destroy( hwndDlg );
  1846. return 0;
  1847. case WM_COMMAND:
  1848. playlist_Command( hwndDlg, wParam, lParam );
  1849. return 0;
  1850. case WM_NOTIFY:
  1851. return playlist_Notify( hwndDlg, wParam, lParam );
  1852. case WM_CONTEXTMENU:
  1853. playlist_ContextMenu( hwndDlg, (HWND)wParam, GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) );
  1854. return 0;
  1855. case WM_ERASEBKGND:
  1856. return 1; //handled by WADlg_DrawChildWindowBorders in WM_PAINT
  1857. case WM_PLAYLIST_RELOAD:
  1858. playlist_Reload();
  1859. return 0;
  1860. case WM_PLAYLIST_UNLOAD:
  1861. playlist_Unload( hwndDlg );
  1862. return 0;
  1863. case WM_DISPLAYCHANGE:
  1864. LayoutWindows( hwndDlg, TRUE );
  1865. return 0;
  1866. case WM_APP + 104:
  1867. {
  1868. playlist_UpdateButtonText( hwndDlg, wParam );
  1869. LayoutWindows( hwndDlg, TRUE );
  1870. return 0;
  1871. }
  1872. case WM_APP + 103:
  1873. {
  1874. int current = -1;
  1875. if ( wParam )
  1876. {
  1877. // TODO need to get this done in the background so as not to lock up the ui on large playlists
  1878. wchar_t fn[ 1024 ] = { 0 };
  1879. int v = playlist_list.GetCount();
  1880. for ( int x = 0; x < v; x++ )
  1881. {
  1882. currentPlaylist.GetItem( x, fn, 1024 );
  1883. if ( !lstrcmpi( fn, ( !lParam ? (LPWSTR)wParam : current_playing ) ) )
  1884. {
  1885. current = x;
  1886. break;
  1887. }
  1888. }
  1889. }
  1890. PostMessage( playlist_list.getwnd(), WM_ML_IPC, current, ML_IPC_SKINNEDLISTVIEW_SETCURRENT );
  1891. }
  1892. return 0;
  1893. case WM_DROPFILES: playlist_DropFiles((HDROP)wParam); return 0;
  1894. case WM_APP+102:
  1895. {
  1896. if ( cloud_avail )
  1897. {
  1898. int width = 27;
  1899. MLCloudColumn_GetWidth( plugin.hwndLibraryParent, &width );
  1900. playlist_list.SetColumnWidth( 1, width );
  1901. MLSkinnedHeader_SetCloudColumn( ListView_GetHeader( playlist_list.getwnd() ), 1 );
  1902. }
  1903. }
  1904. case WM_APP+100:
  1905. AutoSizePlaylistColumns();
  1906. if ( !loaded )
  1907. {
  1908. loaded = true;
  1909. SetWindowRedraw( playlist_list.getwnd(), TRUE );
  1910. }
  1911. return 0;
  1912. case WM_WINDOWPOSCHANGED:
  1913. if ( ( SWP_NOSIZE | SWP_NOMOVE ) != ( ( SWP_NOSIZE | SWP_NOMOVE ) & ( (WINDOWPOS *)lParam )->flags ) || ( SWP_FRAMECHANGED & ( (WINDOWPOS *)lParam )->flags ) )
  1914. {
  1915. LayoutWindows( hwndDlg, !( SWP_NOREDRAW & ( (WINDOWPOS *)lParam )->flags ) );
  1916. }
  1917. return 0;
  1918. case WM_USER + 0x200:
  1919. SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, 1 ); // yes, we support no - redraw resize
  1920. return TRUE;
  1921. case WM_USER + 0x201:
  1922. offsetX = (short)LOWORD( wParam );
  1923. offsetY = (short)HIWORD( wParam );
  1924. g_rgnUpdate = (HRGN)lParam;
  1925. return TRUE;
  1926. case WM_ML_CHILDIPC:
  1927. {
  1928. if ( lParam == ML_CHILDIPC_DROPITEM && wParam )
  1929. {
  1930. mlDropItemStruct *dis = (mlDropItemStruct *)wParam;
  1931. if ( dis )
  1932. {
  1933. switch ( dis->type )
  1934. {
  1935. case ML_TYPE_FILENAMES:
  1936. case ML_TYPE_STREAMNAMES:
  1937. case ML_TYPE_FILENAMESW:
  1938. case ML_TYPE_STREAMNAMESW:
  1939. case ML_TYPE_ITEMRECORDLIST:
  1940. case ML_TYPE_ITEMRECORDLISTW:
  1941. case ML_TYPE_CDTRACKS:
  1942. dis->result = 1;
  1943. break;
  1944. default:
  1945. dis->result = -1;
  1946. break;
  1947. }
  1948. }
  1949. return 0;
  1950. }
  1951. }
  1952. }
  1953. return 0;
  1954. }
  1955. static wchar_t entryFN[ FILENAME_SIZE ];
  1956. static wchar_t titleFN[ FILETITLE_SIZE ];
  1957. static INT_PTR CALLBACK entryProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
  1958. {
  1959. switch ( uMsg )
  1960. {
  1961. case WM_INITDIALOG:
  1962. {
  1963. SendDlgItemMessage( hwndDlg, IDC_OLD, EM_SETLIMITTEXT, FILENAME_SIZE, 0 );
  1964. SendDlgItemMessage( hwndDlg, IDC_NEW, EM_SETLIMITTEXT, FILENAME_SIZE, 0 );
  1965. SendDlgItemMessage( hwndDlg, IDC_OLD_TITLE, EM_SETLIMITTEXT, FILETITLE_SIZE, 0 );
  1966. SendDlgItemMessage( hwndDlg, IDC_NEW_TITLE, EM_SETLIMITTEXT, FILETITLE_SIZE, 0 );
  1967. SetDlgItemTextW( hwndDlg, IDC_OLD, entryFN );
  1968. SetDlgItemTextW( hwndDlg, IDC_OLD_TITLE, titleFN );
  1969. SetDlgItemTextW( hwndDlg, IDC_NEW, entryFN );
  1970. SetDlgItemTextW( hwndDlg, IDC_NEW_TITLE, titleFN );
  1971. return TRUE;
  1972. }
  1973. case WM_COMMAND:
  1974. switch ( LOWORD( wParam ) )
  1975. {
  1976. case IDOK:
  1977. GetDlgItemTextW( hwndDlg, IDC_NEW, entryFN, FILENAME_SIZE );
  1978. GetDlgItemTextW( hwndDlg, IDC_NEW_TITLE, titleFN, FILETITLE_SIZE );
  1979. EndDialog( hwndDlg, 1 );
  1980. return 0;
  1981. case IDCANCEL:
  1982. EndDialog( hwndDlg, 0 );
  1983. return 0;
  1984. case IDC_PLAYLIST_EDIT_ENTRY_BROWSE:
  1985. {
  1986. wchar_t buf[ FILENAME_SIZE ] = { 0 };
  1987. UINT len = GetDlgItemTextW( hwndDlg, IDC_NEW, buf, FILENAME_SIZE ) + 1;
  1988. OPENFILENAME of = { 0 };
  1989. of.lStructSize = sizeof( OPENFILENAME );
  1990. of.hwndOwner = hwndDlg;
  1991. of.nMaxFileTitle = 32;
  1992. of.lpstrFilter = (wchar_t *)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 1, IPC_GET_EXTLISTW );
  1993. of.nMaxCustFilter = 1024;
  1994. of.lpstrFile = buf;
  1995. of.nMaxFile = FILENAME_SIZE;
  1996. of.lpstrTitle = WASABI_API_LNGSTRINGW( IDS_BROWSE_FOR_PLEDIT_ENTRY );
  1997. of.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_ENABLESIZING;
  1998. if ( GetOpenFileName( &of ) )
  1999. {
  2000. wchar_t title[ FILETITLE_SIZE ] = { 0 };
  2001. mediaLibrary.GetFileInfo( buf, title, FILETITLE_SIZE, NULL );
  2002. SetDlgItemTextW( hwndDlg, IDC_NEW_TITLE, title );
  2003. SetDlgItemTextW( hwndDlg, IDC_NEW, buf );
  2004. }
  2005. GlobalFree( (void *)of.lpstrFilter );
  2006. break;
  2007. }
  2008. }
  2009. return 0;
  2010. }
  2011. return 0;
  2012. }
  2013. void EditEntry( HWND parent )
  2014. {
  2015. int i = playlist_list.GetCount();
  2016. while ( i-- )
  2017. {
  2018. if ( playlist_list.GetSelected( i ) )
  2019. {
  2020. currentPlaylist.GetItem( i, entryFN, FILENAME_SIZE );
  2021. currentPlaylist.GetItemTitle( i, titleFN, FILETITLE_SIZE );
  2022. INT_PTR res = WASABI_API_DIALOGBOXW( IDD_EDIT_FN, parent, entryProc );
  2023. if ( res == 1 )
  2024. {
  2025. // if the file has changed, force an update of the item'l_enqueue_file file time since we're not
  2026. // going to allow the view to automatically do such things since with the changes to
  2027. // support editing the title the automatic title lookup after an edit was disabled
  2028. wchar_t filepath[ FILENAME_SIZE ] = { 0 };
  2029. currentPlaylist.GetItem( i, filepath, FILENAME_SIZE );
  2030. if ( lstrcmpi( filepath, entryFN ) )
  2031. {
  2032. int length = -1;
  2033. mediaLibrary.GetFileInfo( entryFN, NULL, NULL, &length );
  2034. currentPlaylist.SetItemLengthMilliseconds( i, length * 1000 );
  2035. }
  2036. currentPlaylist.SetItemFilename( i, entryFN );
  2037. currentPlaylist.SetItemTitle( i, titleFN );
  2038. Changed();
  2039. }
  2040. else
  2041. break;
  2042. }
  2043. }
  2044. SyncPlaylist();
  2045. }
  2046. class DownloadEntry : public ifc_downloadManagerCallback
  2047. {
  2048. public:
  2049. DownloadEntry( const wchar_t *p_url, const wchar_t *p_destination_filepath, const wchar_t *p_title, const wchar_t *p_source )
  2050. {
  2051. this->_url = _wcsdup( p_url );
  2052. this->_destination_filepath = _wcsdup( p_destination_filepath );
  2053. this->_title = _wcsdup( p_title );
  2054. this->_source = _wcsdup( p_source );
  2055. }
  2056. void OnInit( DownloadToken p_token )
  2057. {
  2058. api_httpreceiver *l_http = WAC_API_DOWNLOADMANAGER->GetReceiver( p_token );
  2059. if ( l_http )
  2060. {
  2061. l_http->AllowCompression();
  2062. l_http->addheader( "Accept: */*" );
  2063. }
  2064. }
  2065. void OnConnect( DownloadToken p_token )
  2066. {
  2067. // ---- create file handle
  2068. _hFile = CreateFileW( _destination_filepath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  2069. if ( _hFile == INVALID_HANDLE_VALUE )
  2070. this->cancelDownload( p_token );
  2071. this->getContentLength( p_token );
  2072. }
  2073. void OnTick( DownloadToken p_token )
  2074. {
  2075. static bool l_was_renamed = false;
  2076. const char* l_file_extention = WAC_API_DOWNLOADMANAGER->GetExtention(p_token);
  2077. if (l_file_extention && *l_file_extention)
  2078. {
  2079. wa::strings::wa_string l_destination_filepath_old( _destination_filepath );
  2080. wa::strings::wa_string l_destination_filepath_new( _destination_filepath );
  2081. if ( !l_destination_filepath_new.contains(l_file_extention) )
  2082. {
  2083. l_destination_filepath_new.append( "." );
  2084. l_destination_filepath_new.append(l_file_extention);
  2085. if ( wa::files::file_exists( _destination_filepath.c_str() ) )
  2086. {
  2087. if ( _hFile != INVALID_HANDLE_VALUE )
  2088. CloseHandle( _hFile );
  2089. }
  2090. if ( std::rename( l_destination_filepath_old.GetA().c_str(), l_destination_filepath_new.GetA().c_str() ) )
  2091. {
  2092. _destination_filepath = l_destination_filepath_new.GetW();
  2093. _hFile = CreateFileW( _destination_filepath.c_str(), FILE_APPEND_DATA, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
  2094. }
  2095. }
  2096. }
  2097. }
  2098. void OnData( DownloadToken p_token, void *p_data, size_t p_data_size )
  2099. {
  2100. this->getContentLength( p_token );
  2101. if ( _hFile == INVALID_HANDLE_VALUE )
  2102. {
  2103. // ---- get the file extention
  2104. const char* l_file_extention = WAC_API_DOWNLOADMANAGER->GetExtention(p_token);
  2105. bool l_extension_found = false;
  2106. wa::strings::wa_string l_destination_filepath_old( _destination_filepath );
  2107. wa::strings::wa_string l_destination_filepath_new( _destination_filepath );
  2108. if ( l_file_extention && *l_file_extention )
  2109. {
  2110. if ( !l_destination_filepath_new.contains( l_file_extention ) )
  2111. {
  2112. l_destination_filepath_new.append( "." );
  2113. l_destination_filepath_new.append( l_file_extention );
  2114. l_extension_found = true;
  2115. }
  2116. }
  2117. else
  2118. {
  2119. std::string l_filepath(l_file_extention);
  2120. int l_position = l_filepath.find_first_of( "." );
  2121. if ( l_filepath.size() - l_position > 5 )
  2122. {
  2123. std::string l_url( WAC_API_DOWNLOADMANAGER->GetUrl( p_token ) );
  2124. l_position = l_url.find_last_of( "." );
  2125. if ( l_url.size() - l_position < 5 )
  2126. {
  2127. l_destination_filepath_new.append( "." );
  2128. l_destination_filepath_new.append( l_url.substr( l_position + 1 ) );
  2129. l_extension_found = true;
  2130. }
  2131. }
  2132. }
  2133. if ( l_extension_found )
  2134. {
  2135. if ( wa::files::file_exists( _destination_filepath.c_str() ) )
  2136. {
  2137. if ( _hFile != INVALID_HANDLE_VALUE )
  2138. CloseHandle( _hFile );
  2139. }
  2140. if ( std::rename( l_destination_filepath_old.GetA().c_str(), l_destination_filepath_new.GetA().c_str() ) )
  2141. {
  2142. _destination_filepath = l_destination_filepath_new.GetW();
  2143. }
  2144. }
  2145. // ---- create file handle
  2146. _hFile = CreateFileW( _destination_filepath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  2147. // ---- OnConnect to be removed once dlmgr is fixed
  2148. }
  2149. // ---- OnData
  2150. // ---- if file handle is invalid, then cancel download
  2151. if ( _hFile == INVALID_HANDLE_VALUE )
  2152. {
  2153. this->cancelDownload( p_token );
  2154. return;
  2155. }
  2156. this->_downloaded = (size_t)WAC_API_DOWNLOADMANAGER->GetBytesDownloaded( p_token );
  2157. if ( p_data_size > 0 )
  2158. {
  2159. // ---- hFile is valid handle, and write to disk
  2160. DWORD numWritten = 0;
  2161. WriteFile( _hFile, p_data, (DWORD)p_data_size, &numWritten, FALSE );
  2162. // ---- failed writing the number of datalen characters, cancel download
  2163. if ( numWritten != p_data_size )
  2164. {
  2165. this->cancelDownload( p_token );
  2166. return;
  2167. }
  2168. }
  2169. }
  2170. void OnCancel( DownloadToken p_token )
  2171. {
  2172. if ( _hFile != INVALID_HANDLE_VALUE )
  2173. {
  2174. CloseHandle( _hFile );
  2175. DeleteFileW( _destination_filepath.c_str() );
  2176. }
  2177. this->resumeNextPendingDownload( p_token );
  2178. this->Release();
  2179. }
  2180. void OnError( DownloadToken p_token, int p_error )
  2181. {
  2182. wsprintfW( _log_message_w, L"An error occurs when downloading the file '%s' ( %d ) !", WAC_API_DOWNLOADMANAGER->GetTitle( p_token ), p_error );
  2183. LOG_ERROR( _log_message_w );
  2184. if ( _hFile != INVALID_HANDLE_VALUE )
  2185. {
  2186. CloseHandle( _hFile );
  2187. DeleteFileW( _destination_filepath.c_str() );
  2188. }
  2189. this->resumeNextPendingDownload( p_token );
  2190. this->Release();
  2191. }
  2192. void OnFinish( DownloadToken p_token )
  2193. {
  2194. if ( _hFile != INVALID_HANDLE_VALUE )
  2195. {
  2196. CloseHandle( _hFile );
  2197. _hFile = INVALID_HANDLE_VALUE;
  2198. }
  2199. // Update the playlist with the local path
  2200. const wchar_t *l_file_location = WAC_API_DOWNLOADMANAGER->GetLocation( p_token );
  2201. wa::strings::wa_string l_new_filename( l_file_location );
  2202. l_new_filename.append( "." );
  2203. l_new_filename.append( WAC_API_DOWNLOADMANAGER->GetExtention( p_token ) );
  2204. WAC_API_DOWNLOADMANAGER->SetLocation( p_token, l_new_filename.GetW().c_str() );
  2205. for ( pl_entry *l_pl_entry : currentPlaylist.entries )
  2206. {
  2207. if ( wcscmp( l_pl_entry->filetitle, WAC_API_DOWNLOADMANAGER->GetTitle( p_token ) ) == 0 )
  2208. {
  2209. l_pl_entry->SetFilename( l_file_location );
  2210. if ( AGAVE_API_PLAYLISTMANAGER->Save( currentPlaylistFilename, &currentPlaylist ) != PLAYLISTMANAGER_FAILED )
  2211. playlist_list.RefreshAll();
  2212. break;
  2213. }
  2214. }
  2215. this->resumeNextPendingDownload( p_token );
  2216. this->Release();
  2217. }
  2218. int GetSource( wchar_t *source, size_t source_cch )
  2219. {
  2220. if ( !this->_source.empty() )
  2221. return wcscpy_s( source, source_cch, this->_source.c_str() );
  2222. else
  2223. return 1;
  2224. }
  2225. int GetTitle( wchar_t *title, size_t title_cch )
  2226. {
  2227. return wcscpy_s( title, title_cch, _title.c_str() );
  2228. }
  2229. int GetLocation( wchar_t *location, size_t location_cch )
  2230. {
  2231. return wcscpy_s( location, location_cch, this->_destination_filepath.c_str() );
  2232. }
  2233. size_t AddRef()
  2234. {
  2235. return _ref_count.fetch_add( 1 );
  2236. }
  2237. size_t Release()
  2238. {
  2239. if ( _ref_count.load() == 0 )
  2240. return _ref_count.load();
  2241. std::size_t r = _ref_count.fetch_sub( 1 );
  2242. if ( r == 0 )
  2243. delete( this );
  2244. return r;
  2245. }
  2246. protected:
  2247. RECVS_DISPATCH;
  2248. private:
  2249. inline void resumeNextPendingDownload( DownloadToken p_token )
  2250. {
  2251. {
  2252. AutoLock lock( itemsPlaylistQueueLock );
  2253. size_t l_index = 0;
  2254. for ( DownloadToken &l_download_token : plDownloads )
  2255. {
  2256. if ( l_download_token == p_token )
  2257. {
  2258. plDownloads.erase( plDownloads.begin() + l_index );
  2259. break;
  2260. }
  2261. ++l_index;
  2262. }
  2263. }
  2264. for ( DownloadToken &l_download_token : plDownloads )
  2265. {
  2266. if ( WAC_API_DOWNLOADMANAGER->IsPending( l_download_token ) )
  2267. {
  2268. WAC_API_DOWNLOADMANAGER->ResumePendingDownload( l_download_token );
  2269. break;
  2270. }
  2271. }
  2272. }
  2273. inline void cancelDownload( DownloadToken p_token )
  2274. {
  2275. wsprintfW( _log_message_w, L"The file '%s' was cancelled !", WAC_API_DOWNLOADMANAGER->GetTitle( p_token ) );
  2276. LOG_ERROR( _log_message_w );
  2277. WAC_API_DOWNLOADMANAGER->CancelDownload( p_token );
  2278. this->resumeNextPendingDownload( p_token );
  2279. }
  2280. inline void getContentLength( DownloadToken p_token )
  2281. {
  2282. if ( p_token == NULL )
  2283. return;
  2284. // ---- retrieve total size
  2285. api_httpreceiver* l_http = WAC_API_DOWNLOADMANAGER->GetReceiver(p_token);
  2286. if ( l_http )
  2287. this->_totalSize = l_http->content_length();
  2288. }
  2289. std::wstring _url;
  2290. std::wstring _destination_filepath;
  2291. std::wstring _title;
  2292. std::wstring _source;
  2293. HANDLE _hFile = INVALID_HANDLE_VALUE;
  2294. size_t _totalSize = 0;
  2295. size_t _downloaded = 0;
  2296. std::atomic<std::size_t> _ref_count = 1;
  2297. };
  2298. #define CBCLASS DownloadEntry
  2299. START_DISPATCH;
  2300. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONINIT, OnInit )
  2301. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONCONNECT, OnConnect )
  2302. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONTICK, OnTick )
  2303. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONDATA, OnData )
  2304. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONCANCEL, OnCancel )
  2305. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONFINISH, OnFinish )
  2306. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONERROR, OnError )
  2307. CB( IFC_DOWNLOADMANAGERCALLBACK_GETSOURCE, GetSource )
  2308. CB( IFC_DOWNLOADMANAGERCALLBACK_GETTITLE, GetTitle )
  2309. CB( IFC_DOWNLOADMANAGERCALLBACK_GETLOCATION, GetLocation )
  2310. CB( ADDREF, AddRef )
  2311. CB( RELEASE, Release )
  2312. END_DISPATCH;
  2313. #undef CBCLASS
  2314. void DownloadSelectedEntries( HWND parent )
  2315. {
  2316. int l_count = playlist_list.GetCount();
  2317. pl_entry *l_pl_entry = NULL;
  2318. for ( int i = 0; i < l_count; ++i )
  2319. {
  2320. if ( playlist_list.GetSelected( i ) )
  2321. {
  2322. if ( !currentPlaylist.IsLocal( i ) )
  2323. {
  2324. currentPlaylist.GetItem( i, entryFN, FILENAME_SIZE );
  2325. currentPlaylist.GetItemTitle( i, titleFN, FILETITLE_SIZE );
  2326. if ( WAC_API_DOWNLOADMANAGER )
  2327. {
  2328. wa::strings::wa_string l_url( entryFN );
  2329. TCHAR szPath[ MAX_PATH ];
  2330. if ( SUCCEEDED( SHGetFolderPath( NULL, CSIDL_MYMUSIC, NULL, 0, szPath ) ) )
  2331. {
  2332. l_pl_entry = currentPlaylist.entries[ i ];
  2333. bool l_ext_is_find = false;
  2334. std::string l_s_url( l_url.GetA() );
  2335. wa::strings::wa_string l_full_local_filename( szPath );
  2336. l_full_local_filename.append( PLAYLIST_DOWNLOAD_SUBFOLDER );
  2337. if ( !wa::files::folder_exists( l_full_local_filename.GetA().c_str() ) )
  2338. _mkdir( l_full_local_filename.GetA().c_str() );
  2339. wa::strings::wa_string l_title_filename( titleFN );
  2340. l_title_filename.replaceAll( "/", "_" );
  2341. l_title_filename.replaceAll( "\\", "_" );
  2342. l_title_filename.replaceAll( ":", "_" );
  2343. l_title_filename.replaceAll( "*", "_" );
  2344. l_title_filename.replaceAll( "?", "_" );
  2345. l_title_filename.replaceAll( "\"", "_" );
  2346. l_title_filename.replaceAll( "<", "_" );
  2347. l_title_filename.replaceAll( ">", "_" );
  2348. l_title_filename.replaceAll( "|", "_" );
  2349. l_full_local_filename.append( l_title_filename.GetW() );
  2350. l_ext_is_find = ( l_pl_entry->_extended_infos.count( L"ext" ) == 1 );
  2351. if ( l_ext_is_find )
  2352. {
  2353. wa::strings::wa_string l_file_to_verify( l_full_local_filename.GetW() );
  2354. l_file_to_verify.append( "." );
  2355. l_file_to_verify.append( (*l_pl_entry->_extended_infos.find(L"ext")).second );
  2356. if ( wa::files::file_exists( l_file_to_verify.GetW().c_str() ) )
  2357. {
  2358. l_pl_entry->SetFilename( l_file_to_verify.GetW().c_str() );
  2359. if ( AGAVE_API_PLAYLISTMANAGER->Save( currentPlaylistFilename, &currentPlaylist ) != PLAYLISTMANAGER_FAILED )
  2360. playlist_list.RefreshAll();
  2361. return;
  2362. }
  2363. }
  2364. wa::strings::wa_string l_source( l_s_url );
  2365. l_source.replace( "http://", "" );
  2366. l_source.replace( "https://", "" );
  2367. l_source = l_source.mid( 0, l_source.findFirst( "/" ) );
  2368. wa::strings::wa_string l_redirect_url( WINAMP_REDIRECT_LINK_PROXY_FILE );
  2369. l_redirect_url.append( l_url.GetW() );
  2370. if ( !l_ext_is_find )
  2371. l_redirect_url = l_url.GetW();
  2372. DownloadEntry *_downloadEntry = new DownloadEntry( l_redirect_url.GetW().c_str(), l_full_local_filename.GetW().c_str(), titleFN, l_source.GetW().c_str() );
  2373. if ( plDownloads.size() < SIMULTANEOUS_DOWNLOADS )
  2374. {
  2375. DownloadToken dt = WAC_API_DOWNLOADMANAGER->DownloadEx( l_redirect_url.GetA().c_str(), _downloadEntry, api_downloadManager::DOWNLOADEX_CALLBACK | api_downloadManager::DOWNLOADEX_UI );
  2376. plDownloads.push_back( dt );
  2377. }
  2378. else
  2379. {
  2380. DownloadToken dt = WAC_API_DOWNLOADMANAGER->DownloadEx( l_redirect_url.GetA().c_str(), _downloadEntry, api_downloadManager::DOWNLOADEX_CALLBACK | api_downloadManager::DOWNLOADEX_PENDING | api_downloadManager::DOWNLOADEX_UI );
  2381. plDownloads.push_back( dt );
  2382. }
  2383. }
  2384. }
  2385. }
  2386. }
  2387. }
  2388. }