ArtistAlbumLists.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173
  1. #include "ArtistAlbumLists.h"
  2. #include "Filters.h"
  3. #include "api__ml_pmp.h"
  4. #include "resource1.h"
  5. #include "metadata_utils.h"
  6. extern winampMediaLibraryPlugin plugin;
  7. #define SKIP_THE_AND_WHITESPACE(x) { while (!iswalnum(*x) && *x) x++; if (!_wcsnicmp(x,L"the ",4)) x+=4; while (*x == L' ') x++; }
  8. int STRCMP_NULLOK(const wchar_t *pa, const wchar_t *pb) {
  9. if (!pa) pa=L"";
  10. else SKIP_THE_AND_WHITESPACE(pa)
  11. if (!pb) pb=L"";
  12. else SKIP_THE_AND_WHITESPACE(pb)
  13. return lstrcmpi(pa,pb);
  14. }
  15. #undef SKIP_THE_AND_WHITESPACE
  16. Filter * getFilter(wchar_t *name);
  17. #define RETIFNZ(v) if ((v)<0) return use_dir?1:-1; if ((v)>0) return use_dir?-1:1;
  18. typedef struct
  19. {
  20. songid_t songid;
  21. Device * dev;
  22. } SortSongItem;
  23. int thread_killed = 0;
  24. static int useby, usedir, usecloud;
  25. static Device * currentDev;
  26. static int sortFunc(const void *elem1, const void *elem2)
  27. {
  28. int use_by=useby;
  29. int use_dir=usedir;
  30. songid_t a=(songid_t)*(void **)elem1;
  31. songid_t b=(songid_t)*(void **)elem2;
  32. wchar_t bufa[2048] = {0}, bufb[2048] = {0};
  33. // this might be too slow, but it'd be nice
  34. for (int x = 0; x < 5; x ++)
  35. {
  36. if (thread_killed) break;
  37. bufa[0]=bufb[0]=0;
  38. if (use_by == (7+usecloud)) // year -> artist -> album -> track
  39. {
  40. int v1=currentDev->getTrackYear(a);
  41. int v2=currentDev->getTrackYear(b);
  42. if (v1<0)v1=0;
  43. if (v2<0)v2=0;
  44. RETIFNZ(v1-v2)
  45. use_by=0;
  46. }
  47. else if (use_by == 4 && usecloud) // cloud -> artist -> album -> track
  48. {
  49. currentDev->getTrackExtraInfo(a,L"cloud",bufa,ARRAYSIZE(bufa));
  50. currentDev->getTrackExtraInfo(b,L"cloud",bufb,ARRAYSIZE(bufb));
  51. int v=STRCMP_NULLOK(bufa,bufb);
  52. RETIFNZ(v)
  53. use_by=0;
  54. }
  55. else if (use_by == 1) // title -> artist -> album -> disc -> track
  56. {
  57. currentDev->getTrackTitle(a,bufa,2048);
  58. currentDev->getTrackTitle(b,bufb,2048);
  59. int v=STRCMP_NULLOK(bufa,bufb);
  60. RETIFNZ(v)
  61. use_by=0;
  62. }
  63. else if (use_by == 0) // artist -> album -> disc -> track -> title
  64. {
  65. currentDev->getTrackArtist(a,bufa,2048);
  66. currentDev->getTrackArtist(b,bufb,2048);
  67. int v=STRCMP_NULLOK(bufa,bufb);
  68. RETIFNZ(v)
  69. use_by=2;
  70. }
  71. else if (use_by == 2) // album -> disc -> track -> title -> artist
  72. {
  73. currentDev->getTrackAlbum(a,bufa,2048);
  74. currentDev->getTrackAlbum(b,bufb,2048);
  75. int v=STRCMP_NULLOK(bufa,bufb);
  76. RETIFNZ(v)
  77. use_dir=0;
  78. use_by=5;
  79. }
  80. else if (use_by == 5+usecloud) // disc -> track -> title -> artist -> album
  81. {
  82. int v1=currentDev->getTrackDiscNum(a);
  83. int v2=currentDev->getTrackDiscNum(b);
  84. if (v1<0)v1=0;
  85. if (v2<0)v2=0;
  86. RETIFNZ(v1-v2)
  87. use_by=4;
  88. }
  89. else if (use_by == 4+usecloud) // track -> title -> artist -> album -> disc
  90. {
  91. int v1=currentDev->getTrackTrackNum(a);
  92. int v2=currentDev->getTrackTrackNum(b);
  93. if (v1<0)v1=0;
  94. if (v2<0)v2=0;
  95. RETIFNZ(v1-v2)
  96. use_by=1;
  97. }
  98. else if (use_by == 6+usecloud) // genre -> artist -> album -> disc -> track
  99. {
  100. currentDev->getTrackGenre(a,bufa,2048);
  101. currentDev->getTrackGenre(b,bufb,2048);
  102. int v=STRCMP_NULLOK(bufa,bufb);
  103. RETIFNZ(v)
  104. use_by=0;
  105. }
  106. else if (use_by == 3) // length -> artist -> album -> disc -> track
  107. {
  108. int v1=currentDev->getTrackLength(a);
  109. int v2=currentDev->getTrackLength(b);
  110. if (v1<0)v1=0;
  111. if (v2<0)v2=0;
  112. RETIFNZ(v1-v2)
  113. use_by=0;
  114. }
  115. else if (use_by == (8+usecloud)) // bitrate -> artist -> album -> disc -> track
  116. {
  117. int v1=currentDev->getTrackBitrate(a);
  118. int v2=currentDev->getTrackBitrate(b);
  119. if (v1<0)v1=0;
  120. if (v2<0)v2=0;
  121. RETIFNZ(v1-v2)
  122. use_by=0;
  123. }
  124. else if (use_by == (9+usecloud)) // size -> artist -> album -> disc -> track
  125. {
  126. __int64 v1=currentDev->getTrackSize(a);
  127. __int64 v2=currentDev->getTrackSize(b);
  128. if (v1<0)v1=0;
  129. if (v2<0)v2=0;
  130. RETIFNZ(v1-v2)
  131. use_by=0;
  132. }
  133. else if (use_by == (10+usecloud)) // playcount -> artist -> album -> disc -> track
  134. {
  135. int v1=currentDev->getTrackPlayCount(a);
  136. int v2=currentDev->getTrackPlayCount(b);
  137. if (v1<0)v1=0;
  138. if (v2<0)v2=0;
  139. RETIFNZ(v1-v2)
  140. use_by=0;
  141. }
  142. else if (use_by == (11+usecloud)) // rating -> artist -> album -> disc -> track
  143. {
  144. int v1=currentDev->getTrackRating(a);
  145. int v2=currentDev->getTrackRating(b);
  146. if (v1<0)v1=0;
  147. if (v2<0)v2=0;
  148. RETIFNZ(v1-v2)
  149. use_by=0;
  150. }
  151. else if (use_by == (12+usecloud))
  152. {
  153. double v = difftime((time_t)currentDev->getTrackLastPlayed(a),(time_t)currentDev->getTrackLastPlayed(b));
  154. RETIFNZ(v);
  155. use_by=0;
  156. }
  157. else if (use_by == (13+usecloud)) // album artist -> album
  158. {
  159. currentDev->getTrackAlbumArtist(a,bufa,ARRAYSIZE(bufa));
  160. currentDev->getTrackAlbumArtist(b,bufb,ARRAYSIZE(bufb));
  161. int v=STRCMP_NULLOK(bufa,bufb);
  162. RETIFNZ(v)
  163. use_by=2;
  164. }
  165. else if (use_by == (14+usecloud)) // publisher -> album
  166. {
  167. currentDev->getTrackPublisher(a,bufa,ARRAYSIZE(bufa));
  168. currentDev->getTrackPublisher(b,bufb,ARRAYSIZE(bufb));
  169. int v=STRCMP_NULLOK(bufa,bufb);
  170. RETIFNZ(v)
  171. use_by=2;
  172. }
  173. else if (use_by == (15+usecloud)) // composer -> album
  174. {
  175. currentDev->getTrackComposer(a,bufa,ARRAYSIZE(bufa));
  176. currentDev->getTrackComposer(b,bufb,ARRAYSIZE(bufb));
  177. int v=STRCMP_NULLOK(bufa,bufb);
  178. RETIFNZ(v)
  179. use_by=2;
  180. }
  181. else if (use_by == (16+usecloud)) // mime -> artist -> album -> disc -> track
  182. {
  183. currentDev->getTrackMimeType(a,bufa,ARRAYSIZE(bufa));
  184. currentDev->getTrackMimeType(b,bufb,ARRAYSIZE(bufb));
  185. int v=STRCMP_NULLOK(bufa,bufb);
  186. RETIFNZ(v)
  187. use_by=2;
  188. }
  189. else if (use_by == (17+usecloud)) // date added -> artist -> album -> disc -> track
  190. {
  191. double v = difftime((time_t)currentDev->getTrackDateAdded(a),(time_t)currentDev->getTrackDateAdded(b));
  192. RETIFNZ(v);
  193. use_by=0;
  194. }
  195. else break; // no sort order?
  196. if (thread_killed) break;
  197. }
  198. return 0;
  199. }
  200. static int sortFunc_filteritems(const void *elem1, const void *elem2) {
  201. FilterItem *a=(FilterItem *)*(void **)elem1;
  202. FilterItem *b=(FilterItem *)*(void **)elem2;
  203. return a->compareTo2(b,useby,usedir);
  204. }
  205. class FilterList : public ListContents {
  206. public:
  207. Filter * filter;
  208. C_ItemList * items;
  209. ArtistAlbumLists * aaList;
  210. wchar_t topString[128];
  211. int nextFilterNum;
  212. int tracks;
  213. int sortcol;
  214. int sortdir;
  215. int id;
  216. FilterList(int id, Device * dev0, C_Config * config, Filter * filter, ArtistAlbumLists * aaList, bool cloud) :
  217. id(id), filter(filter), sortcol(0), sortdir(0), aaList(aaList), tracks(0), nextFilterNum(0), items(0) {
  218. this->topString[0] = 0;
  219. this->config = config;
  220. this->dev = dev0;
  221. this->cloud = cloud;
  222. this->cloudcol = -1;
  223. filter->AddColumns(dev, &fields, config, !!this->cloud);
  224. for(int i = 0; i < fields.GetSize(); i++) {
  225. if (!lstrcmpi(((ListField *)fields.Get(i))->name, L"cloud"))
  226. {
  227. this->cloudcol = ((ListField *)fields.Get(i))->pos;
  228. break;
  229. }
  230. }
  231. wchar_t temp[16] = {0};
  232. wsprintf(temp, L"filter%d_sortcol", id);
  233. this->sortcol = config->ReadInt(temp, 0);
  234. wsprintf(temp, L"filter%d_sortdir", id);
  235. this->sortdir = config->ReadInt(temp, 0);
  236. this->SortColumns();
  237. }
  238. virtual ~FilterList() {
  239. wchar_t temp[16] = {0};
  240. wsprintf(temp, L"filter%d_sortcol", id);
  241. config->WriteInt(temp, sortcol);
  242. wsprintf(temp, L"filter%d_sortdir", id);
  243. config->WriteInt(temp, sortdir);
  244. }
  245. virtual int GetNumColumns() { return fields.GetSize(); }
  246. virtual int GetNumRows() { return items->GetSize() + (filter->HaveTopItem()?1:0); }
  247. virtual wchar_t * GetColumnTitle(int num) {
  248. if(num >=0 && num < fields.GetSize())
  249. return ((ListField *)fields.Get(num))->name;
  250. return L"";
  251. }
  252. virtual int GetColumnWidth(int num) {
  253. if(num >=0 && num < fields.GetSize())
  254. return ((ListField *)fields.Get(num))->width;
  255. return 0;
  256. }
  257. virtual void GetCellText(int row, int col, wchar_t * buf, int buflen) {
  258. buf[0]=0;
  259. if(col >= fields.GetSize() || aaList->bgThread_Handle) return;
  260. int colid = ((ListField*)fields.Get(col))->field;
  261. if(filter->HaveTopItem()) {
  262. if(row) ((FilterItem*)items->Get(row-1))->GetCellText(colid,buf,buflen);
  263. else {
  264. if(colid%100 == 0) lstrcpyn(buf,topString,buflen);
  265. else if(colid%100 == 41) wsprintf(buf,L"%d",tracks);
  266. else if(colid%100 == 40 && filter->nextFilter) wsprintf(buf,L"%d",nextFilterNum);
  267. }
  268. } else ((FilterItem*)items->Get(row))->GetCellText(colid,buf,buflen);
  269. }
  270. virtual void SortList() {
  271. if (sortcol > fields.GetSize()) return;
  272. useby=((ListField*)fields.Get(sortcol))->field;
  273. usedir=sortdir;
  274. qsort(items->GetAll(),items->GetSize(),sizeof(void*),sortFunc_filteritems);
  275. }
  276. virtual int GetSortDirection() { return sortdir; }
  277. virtual int GetSortColumn() { return sortcol; }
  278. virtual void ColumnClicked(int col) {
  279. if(col == sortcol)
  280. sortdir = sortdir?0:1;
  281. else {
  282. sortdir=0;
  283. sortcol=col;
  284. }
  285. SortList();
  286. }
  287. virtual void ColumnResize(int col, int newWidth) {
  288. if(col >=0 && col < fields.GetSize()) {
  289. ListField * lf = (ListField *)fields.Get(col);
  290. lf->width = newWidth;
  291. wchar_t buf[100] = {0};
  292. wsprintf(buf,L"colWidth_%d",lf->field);
  293. config->WriteInt(buf,newWidth);
  294. }
  295. }
  296. virtual pmpart_t GetArt(int row) { return ((FilterItem*)items->Get(row))->GetArt(); }
  297. virtual void SetMode(int mode) {
  298. filter->SetMode(mode);
  299. int i=fields.GetSize();
  300. while(i>=0) { i--; delete (ListField*)fields.Get(i); fields.Del(i); }
  301. filter->AddColumns(dev, &fields, config, !!this->cloud);
  302. SortColumns();
  303. }
  304. virtual songid_t GetTrack(int pos) { return 0; }
  305. };
  306. static void getStars(int stars, wchar_t * buf, int buflen) {
  307. wchar_t * r=L"";
  308. switch(stars) {
  309. case 1: r=L"\u2605"; break;
  310. case 2: r=L"\u2605\u2605"; break;
  311. case 3: r=L"\u2605\u2605\u2605"; break;
  312. case 4: r=L"\u2605\u2605\u2605\u2605"; break;
  313. case 5: r=L"\u2605\u2605\u2605\u2605\u2605"; break;
  314. }
  315. lstrcpyn(buf,r,buflen);
  316. }
  317. extern void timeToString(__time64_t time, wchar_t * buf, int buflen);
  318. static void timeValue(int totalsecs, wchar_t *dest)
  319. {
  320. int secs=totalsecs%60;
  321. int mins=(totalsecs/60)%60;
  322. int hours=(totalsecs/3600)%24;
  323. int days=(totalsecs/86400);
  324. if(days==0) {
  325. wsprintf(dest,L"%d:%02d:%02d",hours,mins,secs);
  326. } else if(days==1) {
  327. wsprintf(dest,L"%d day+%d:%02d:%02d",days,hours,mins,secs);
  328. } else {
  329. wsprintf(dest,L"%d days+%d:%02d:%02d",days,hours,mins,secs);
  330. }
  331. }
  332. void GetInfoString(wchar_t * buf, Device * dev, int numTracks, __int64 totalSize, int totalPlayLength, int cloud) {
  333. wchar_t lengthStr[100]=L"";
  334. wchar_t sizeStr[100]=L"";
  335. wchar_t availStr[100]=L"";
  336. wchar_t devCapacityStr[100]=L"";
  337. int usedPercent;
  338. int fieldsBits = (int)dev->extraActions(DEVICE_SUPPORTED_METADATA,0,0,0);
  339. if(!fieldsBits || (fieldsBits & SUPPORTS_LENGTH)) {
  340. lengthStr[0] = L'[';
  341. timeValue(totalPlayLength,&lengthStr[1]);
  342. wcscat_s(lengthStr,100,L"] ");
  343. }
  344. __int64 available = dev->getDeviceCapacityAvailable();
  345. WASABI_API_LNG->FormattedSizeString(sizeStr, ARRAYSIZE(sizeStr), totalSize);
  346. WASABI_API_LNG->FormattedSizeString(availStr, ARRAYSIZE(availStr), available);
  347. __int64 capacity = dev->getDeviceCapacityTotal();
  348. WASABI_API_LNG->FormattedSizeString(devCapacityStr, ARRAYSIZE(devCapacityStr), capacity);
  349. if(capacity > 0) usedPercent = (int)((((__int64)100)*available) / capacity);
  350. else usedPercent = 0;
  351. if (!cloud || cloud && available > 0)
  352. wsprintf(buf, WASABI_API_LNGSTRINGW(IDS_X_ITEMS_X_AVAILABLE), numTracks, lengthStr, sizeStr, availStr, usedPercent, devCapacityStr);
  353. else
  354. wsprintf(buf, WASABI_API_LNGSTRINGW(IDS_X_ITEMS_X_AVAILABLE_SLIM), numTracks, lengthStr, sizeStr);
  355. }
  356. class TracksList : public PrimaryListContents {
  357. public:
  358. __int64 totalSize;
  359. int totalPlayLength;
  360. int sortcol;
  361. int sortdir;
  362. C_ItemList * tracks;
  363. Device * dev;
  364. ArtistAlbumLists * aaList;
  365. TracksList(Device * dev,C_Config * config, ArtistAlbumLists * aaList, bool cloud) :
  366. dev(dev), aaList(aaList), totalSize(0), totalPlayLength(0), tracks(0) {
  367. this->config = config;
  368. this->cloud = cloud;
  369. this->cloudcol = -1;
  370. this->sortcol = config->ReadInt(L"sortcol", 0);
  371. this->sortdir = config->ReadInt(L"sortdir", 0);
  372. int fieldsBits = (int)dev->extraActions(DEVICE_SUPPORTED_METADATA,0,0,0);
  373. if (!fieldsBits) fieldsBits = -1;
  374. if (fieldsBits & SUPPORTS_ARTIST) fields.Add(new ListField(0, 200, WASABI_API_LNGSTRINGW(IDS_ARTIST), config));
  375. if (fieldsBits & SUPPORTS_TITLE) fields.Add(new ListField(1, 200, WASABI_API_LNGSTRINGW(IDS_TITLE), config));
  376. if (fieldsBits & SUPPORTS_ALBUM) fields.Add(new ListField(2, 200, WASABI_API_LNGSTRINGW(IDS_ALBUM), config));
  377. if (fieldsBits & SUPPORTS_LENGTH) fields.Add(new ListField(3, 64, WASABI_API_LNGSTRINGW(IDS_LENGTH), config));
  378. if (cloud) fields.Add(new ListField(3+cloud, 27, WASABI_API_LNGSTRINGW(IDS_CLOUD), config));
  379. if (fieldsBits & SUPPORTS_TRACKNUM) fields.Add(new ListField(4+cloud, 50, WASABI_API_LNGSTRINGW(IDS_TRACK_NUMBER), config));
  380. if (fieldsBits & SUPPORTS_DISCNUM) fields.Add(new ListField(5+cloud, 38, WASABI_API_LNGSTRINGW(IDS_DISC), config));
  381. if (fieldsBits & SUPPORTS_GENRE) fields.Add(new ListField(6+cloud, 100, WASABI_API_LNGSTRINGW(IDS_GENRE), config));
  382. if (fieldsBits & SUPPORTS_YEAR) fields.Add(new ListField(7+cloud, 38, WASABI_API_LNGSTRINGW(IDS_YEAR), config));
  383. if (fieldsBits & SUPPORTS_BITRATE) fields.Add(new ListField(8+cloud, 45, WASABI_API_LNGSTRINGW(IDS_BITRATE), config));
  384. if (fieldsBits & SUPPORTS_SIZE) fields.Add(new ListField(9+cloud, 90, WASABI_API_LNGSTRINGW(IDS_SIZE), config));
  385. if (fieldsBits & SUPPORTS_PLAYCOUNT) fields.Add(new ListField(10+cloud, 64, WASABI_API_LNGSTRINGW(IDS_PLAY_COUNT), config));
  386. if (fieldsBits & SUPPORTS_RATING) fields.Add(new ListField(11+cloud, 64, WASABI_API_LNGSTRINGW(IDS_RATING), config));
  387. if (fieldsBits & SUPPORTS_LASTPLAYED) fields.Add(new ListField(12+cloud, 120, WASABI_API_LNGSTRINGW(IDS_LAST_PLAYED), config));
  388. if (fieldsBits & SUPPORTS_ALBUMARTIST) fields.Add(new ListField(13+cloud, 200, WASABI_API_LNGSTRINGW(IDS_ALBUM_ARTIST), config, true));
  389. if (fieldsBits & SUPPORTS_PUBLISHER) fields.Add(new ListField(14+cloud, 200, WASABI_API_LNGSTRINGW(IDS_PUBLISHER), config, true));
  390. if (fieldsBits & SUPPORTS_COMPOSER) fields.Add(new ListField(15+cloud, 200, WASABI_API_LNGSTRINGW(IDS_COMPOSER), config, true));
  391. if (fieldsBits & SUPPORTS_MIMETYPE) fields.Add(new ListField(16+cloud, 100, WASABI_API_LNGSTRINGW(IDS_MIME_TYPE), config, true));
  392. if (fieldsBits & SUPPORTS_DATEADDED) fields.Add(new ListField(17+cloud, 120, WASABI_API_LNGSTRINGW(IDS_DATE_ADDED), config, true));
  393. this->SortColumns();
  394. if (cloud)
  395. {
  396. // not pretty but it'll allow us to know the current
  397. // position of the cloud column for drawing purposes
  398. for(int i = 0; i < fields.GetSize(); i++) {
  399. if (!lstrcmpi(((ListField *)fields.Get(i))->name, L"cloud"))
  400. {
  401. this->cloudcol = ((ListField *)fields.Get(i))->pos;
  402. break;
  403. }
  404. }
  405. }
  406. }
  407. virtual ~TracksList() {
  408. config->WriteInt(L"sortcol", sortcol);
  409. config->WriteInt(L"sortdir", sortdir);
  410. }
  411. virtual int GetNumColumns() { return fields.GetSize(); }
  412. virtual int GetNumRows() { return (tracks ? tracks->GetSize() : 0); }
  413. virtual int GetColumnWidth(int num) {
  414. if(num >=0 && num < fields.GetSize())
  415. return ((ListField *)fields.Get(num))->width;
  416. return 0;
  417. }
  418. virtual wchar_t * GetColumnTitle(int num) {
  419. if(num >=0 && num < fields.GetSize())
  420. return ((ListField *)fields.Get(num))->name;
  421. return L"";
  422. }
  423. virtual void GetCellText(int row, int col, wchar_t * buf, int buflen) {
  424. buf[0]=0;
  425. if(row >= tracks->GetSize() || aaList->bgThread_Handle) return;
  426. songid_t s = (songid_t)tracks->Get(row);
  427. if(col >=0 && col < fields.GetSize()) {
  428. if (cloud)
  429. {
  430. switch(((ListField *)fields.Get(col))->field) {
  431. case 0: dev->getTrackArtist(s,buf,buflen); return;
  432. case 1: dev->getTrackTitle(s,buf,buflen); return;
  433. case 2: dev->getTrackAlbum(s,buf,buflen); return;
  434. case 3: { int l=dev->getTrackLength(s); if (l>=0) wsprintf(buf,L"%d:%02d",l/1000/60,(l/1000)%60); return; }
  435. case 4: { dev->getTrackExtraInfo(s,L"cloud",buf,buflen); return; }
  436. case 5: { int t=dev->getTrackTrackNum(s); if (t>0) wsprintf(buf,L"%d",t); return; }
  437. case 6: { int d = dev->getTrackDiscNum(s); if(d>0) wsprintf(buf,L"%d",d); return; }
  438. case 7: dev->getTrackGenre(s,buf,buflen); return;
  439. case 8: { int d = dev->getTrackYear(s); if(d>0) wsprintf(buf,L"%d",d); return; }
  440. case 9: { int d = dev->getTrackBitrate(s); if(d>0) wsprintf(buf,WASABI_API_LNGSTRINGW(IDS_KBPS),d); return; }
  441. case 10: WASABI_API_LNG->FormattedSizeString(buf, buflen, dev->getTrackSize(s)); return;
  442. case 11: { int d = dev->getTrackPlayCount(s); if(d>=0) wsprintf(buf,L"%d",d); return; }
  443. case 12: getStars(dev->getTrackRating(s),buf,buflen); return;
  444. case 13: timeToString(dev->getTrackLastPlayed(s),buf,buflen); return;
  445. case 14: dev->getTrackAlbumArtist(s,buf,buflen); return;
  446. case 15: dev->getTrackPublisher(s,buf,buflen); return;
  447. case 16: dev->getTrackComposer(s,buf,buflen); return;
  448. case 17: dev->getTrackMimeType(s,buf,buflen); return;
  449. case 18: timeToString(dev->getTrackDateAdded(s),buf,buflen); return;
  450. }
  451. }
  452. else
  453. {
  454. switch(((ListField *)fields.Get(col))->field) {
  455. case 0: dev->getTrackArtist(s,buf,buflen); return;
  456. case 1: dev->getTrackTitle(s,buf,buflen); return;
  457. case 2: dev->getTrackAlbum(s,buf,buflen); return;
  458. case 3: { int l=dev->getTrackLength(s); wsprintf(buf,L"%d:%02d",l/1000/60,(l/1000)%60); return; }
  459. case 4: { int t=dev->getTrackTrackNum(s); if (t>0) wsprintf(buf,L"%d",t); return; }
  460. case 5: { int d = dev->getTrackDiscNum(s); if(d>0) wsprintf(buf,L"%d",d); return; }
  461. case 6: dev->getTrackGenre(s,buf,buflen); return;
  462. case 7: { int d = dev->getTrackYear(s); if(d>0) wsprintf(buf,L"%d",d); return; }
  463. case 8: { int d = dev->getTrackBitrate(s); if(d>0) wsprintf(buf,WASABI_API_LNGSTRINGW(IDS_KBPS),d); return; }
  464. case 9: WASABI_API_LNG->FormattedSizeString(buf, buflen, dev->getTrackSize(s)); return;
  465. case 10: { int d = dev->getTrackPlayCount(s); if(d>=0) wsprintf(buf,L"%d",d); return; }
  466. case 11: getStars(dev->getTrackRating(s),buf,buflen); return;
  467. case 12: timeToString(dev->getTrackLastPlayed(s),buf,buflen); return;
  468. case 13: dev->getTrackAlbumArtist(s,buf,buflen); return;
  469. case 14: dev->getTrackPublisher(s,buf,buflen); return;
  470. case 15: dev->getTrackComposer(s,buf,buflen); return;
  471. case 16: dev->getTrackMimeType(s,buf,buflen); return;
  472. case 17: timeToString(dev->getTrackDateAdded(s),buf,buflen); return;
  473. }
  474. }
  475. }
  476. }
  477. virtual void SortList() {
  478. useby = ((ListField*)fields.Get(sortcol))->field;
  479. usedir = sortdir;
  480. // if a cloud item then adjust things as needed
  481. // since we're inserting between genre and year
  482. usecloud = cloud;
  483. thread_killed = 0;
  484. currentDev = dev;
  485. qsort(tracks->GetAll(),tracks->GetSize(),sizeof(void*),sortFunc);
  486. }
  487. virtual void ColumnResize(int col, int newWidth) {
  488. if(col >=0 && col < fields.GetSize()) {
  489. ListField * lf = (ListField *)fields.Get(col);
  490. lf->width = newWidth;
  491. wchar_t buf[100] = {0};
  492. wsprintf(buf,L"colWidth_%d",lf->field);
  493. config->WriteInt(buf,newWidth);
  494. }
  495. }
  496. virtual int GetSortColumn() { return sortcol; }
  497. virtual int GetSortDirection() { return sortdir; }
  498. virtual void ColumnClicked(int col) {
  499. if(col == sortcol)
  500. sortdir = sortdir?0:1;
  501. else {
  502. sortdir=0;
  503. sortcol=col;
  504. }
  505. SortList();
  506. }
  507. virtual void GetInfoString(wchar_t * buf) {
  508. ::GetInfoString(buf, dev, tracks->GetSize(), totalSize, totalPlayLength, cloud);
  509. }
  510. virtual songid_t GetTrack(int pos) { return (songid_t)tracks->Get(pos); }
  511. virtual void RemoveTrack(songid_t song) {
  512. for(int i=0; i<tracks->GetSize(); i++) {
  513. if((songid_t)tracks->Get(i) == song) tracks->Del(i--);
  514. }
  515. }
  516. };
  517. static void FreeFilterItemList(C_ItemList * list) {
  518. if(!list) return;
  519. for(int i=0; i < list->GetSize(); i++) {
  520. FilterItem * a = (FilterItem*)list->Get(i);
  521. if(a->nextFilter) FreeFilterItemList(a->nextFilter); a->nextFilter=0;
  522. delete a;
  523. }
  524. delete list;
  525. }
  526. static DWORD WINAPI bgThreadSearchProc(void *tmp)
  527. {
  528. ArtistAlbumLists *aacList = (ArtistAlbumLists *)tmp;
  529. return (aacList ? aacList->bgSearchThreadProc(tmp) : 0);
  530. }
  531. static DWORD WINAPI bgThreadLoadProc(void *tmp)
  532. {
  533. ArtistAlbumLists *aacList = (ArtistAlbumLists *)tmp;
  534. return (aacList ? aacList->bgLoadThreadProc(tmp) : 0);
  535. }
  536. static DWORD WINAPI bgThreadRefineProc(void *tmp)
  537. {
  538. ArtistAlbumLists *aacList = (ArtistAlbumLists *)tmp;
  539. return (aacList ? aacList->bgRefineThreadProc(tmp) : 0);
  540. }
  541. extern HWND hwndMediaView;
  542. DWORD WINAPI ArtistAlbumLists::bgLoadThreadProc(void *tmp)
  543. {
  544. int l = dev->getPlaylistLength(playlistId);
  545. for(int i=0; i<l; i++) {
  546. songid_t x=dev->getPlaylistTrack(playlistId,i);
  547. int t = dev->getTrackType(x);
  548. if(type != -1 && t != type) continue;
  549. searchedTracks->Add((void*)x);
  550. trackList->Add((void*)x);
  551. unrefinedTracks->Add((void*)x);
  552. }
  553. for(int i=0; i<numFilters; i++) {
  554. if(i==0) {
  555. if (filters[0]) FreeFilterItemList(filters[0]->items);
  556. filters[0]->items = CompilePrimaryList(searchedTracks);
  557. }
  558. else {
  559. delete filters[i]->items;
  560. filters[i]->items = CompileSecondaryList(filters[i-1]->items,i,true);
  561. }
  562. filters[i]->SortList();
  563. }
  564. SetRefine(L"");
  565. if (!bgThread_Kill) PostMessage(hwndMediaView, WM_APP + 3, 0x69, 0);
  566. return 0;
  567. }
  568. DWORD WINAPI ArtistAlbumLists::bgSearchThreadProc(void *tmp)
  569. {
  570. int l = dev->getPlaylistLength(playlistId);
  571. C_ItemList allTracks;
  572. for(int i=0; i<l; i++) {
  573. songid_t x=dev->getPlaylistTrack(playlistId,i);
  574. int t = dev->getTrackType(x);
  575. if(type != -1 && t != type) continue;
  576. allTracks.Add((void*)x);
  577. }
  578. searchedTracks = FilterSongs(lastSearch, &allTracks);
  579. delete unrefinedTracks;
  580. unrefinedTracks = new C_ItemList;
  581. l=searchedTracks->GetSize();
  582. for(int i=0; i<l; i++) unrefinedTracks->Add(searchedTracks->Get(i));
  583. for(int i=0; i<numFilters; i++) {
  584. if(i==0) {
  585. FreeFilterItemList(filters[0]->items);
  586. filters[0]->items = CompilePrimaryList(searchedTracks);
  587. }
  588. else {
  589. delete filters[i]->items;
  590. filters[i]->items = CompileSecondaryList(filters[i-1]->items,i,true);
  591. }
  592. }
  593. SetRefine(L"");
  594. for(int i=0; i<numFilters; i++) filters[i]->SortList();
  595. if (!bgThread_Kill) PostMessage(hwndMediaView, WM_APP + 3, 0x69, 0);
  596. return 0;
  597. }
  598. DWORD WINAPI ArtistAlbumLists::bgRefineThreadProc(void *tmp)
  599. {
  600. SetRefine(lastRefine);
  601. if (!bgThread_Kill) PostMessage(hwndMediaView, WM_APP + 3, 0x69, 0);
  602. return 0;
  603. }
  604. void ArtistAlbumLists::bgQuery_Stop() // exported for other people to call since it is useful (eventually
  605. {
  606. KillTimer(hwndMediaView, 123);
  607. if (bgThread_Handle)
  608. {
  609. thread_killed = bgThread_Kill = 1;
  610. WaitForSingleObject(bgThread_Handle, INFINITE);
  611. CloseHandle(bgThread_Handle);
  612. bgThread_Handle = 0;
  613. }
  614. }
  615. void ArtistAlbumLists::bgQuery(int mode) // only internal used
  616. {
  617. bgQuery_Stop();
  618. // TODO cache the HWND to avoid confusion
  619. SetTimer(hwndMediaView, 123, 200, NULL);
  620. DWORD id;
  621. bgThread_Kill = 0;
  622. bgThread_Handle = CreateThread(NULL, 0, (!mode ? bgThreadLoadProc : (mode == 1 ? bgThreadSearchProc : bgThreadRefineProc)), (LPVOID)this, 0, &id);
  623. }
  624. ArtistAlbumLists::ArtistAlbumLists(Device * dev, int playlistId, C_Config * config, wchar_t ** filterNames, int numFilters0, int type, bool async) {
  625. this->type = type;
  626. this->dev = dev;
  627. this->playlistId = playlistId;
  628. this->numFilters = numFilters0;
  629. this->bgThread_Handle = 0;
  630. this->async = async;
  631. this->lastSearch = 0;
  632. this->lastRefine = 0;
  633. ZeroMemory(&filters, sizeof(filters));
  634. if (config->ReadInt(L"savefilter", 1))
  635. {
  636. lastSearch = wcsdup(config->ReadString(L"savedfilter", L""));
  637. lastRefine = wcsdup(config->ReadString(L"savedrefinefilter", L""));
  638. }
  639. Filter * f = NULL;
  640. for(int i = 0; i < numFilters; i++) {
  641. if(!i) { f = firstFilter = getFilter(filterNames[i]); }
  642. else { f->nextFilter = getFilter(filterNames[i]); f = f->nextFilter; }
  643. }
  644. f = firstFilter;
  645. for(int i=0; i<numFilters; i++) {
  646. filters[i] = new FilterList(i,dev,config,f,this,async);
  647. f = f->nextFilter;
  648. }
  649. tracksLC = new TracksList(dev,config,this,async);
  650. searchedTracks = new C_ItemList;
  651. trackList = new C_ItemList;
  652. unrefinedTracks = new C_ItemList;
  653. if (!async && (!lastSearch || lastSearch && !*lastSearch))
  654. {
  655. int l = dev->getPlaylistLength(playlistId);
  656. for(int i=0; i<l; i++) {
  657. songid_t x=dev->getPlaylistTrack(playlistId,i);
  658. int t = dev->getTrackType(x);
  659. if(type != -1 && t != type) continue;
  660. searchedTracks->Add((void*)x);
  661. trackList->Add((void*)x);
  662. unrefinedTracks->Add((void*)x);
  663. }
  664. }
  665. for(int i=0; i<numFilters; i++) {
  666. if(i==0) filters[i]->items = CompilePrimaryList(searchedTracks);
  667. else filters[i]->items = CompileSecondaryList(filters[i-1]->items,i,true);
  668. filters[i]->SortList();
  669. }
  670. tracksLC->tracks = trackList;
  671. tracksLC->dev=dev;
  672. if (async) bgQuery();
  673. else SetRefine((lastRefine ? lastRefine : L""));
  674. }
  675. ArtistAlbumLists::~ArtistAlbumLists() {
  676. if(numFilters && filters[0]) FreeFilterItemList(filters[0]->items);
  677. for(int i=0; i<numFilters; i++) {
  678. delete filters[i]->filter;
  679. if(i!=0) delete filters[i]->items;
  680. delete filters[i];
  681. }
  682. delete trackList;
  683. delete searchedTracks;
  684. delete unrefinedTracks;
  685. delete tracksLC;
  686. if (lastSearch) free(lastSearch);
  687. if (lastRefine) free(lastRefine);
  688. }
  689. static void parsequicksearch(wchar_t *out, wchar_t *in) // parses a list into a list of terms that we are searching for
  690. {
  691. int inquotes=0, neednull=0;
  692. while (in && *in)
  693. {
  694. wchar_t c=*in++;
  695. if (c != L' ' && c != L'\t' && c != L'\"')
  696. {
  697. neednull=1;
  698. *out++=c;
  699. }
  700. else if (c == L'\"')
  701. {
  702. inquotes=!inquotes;
  703. if (!inquotes)
  704. {
  705. *out++=0;
  706. neednull=0;
  707. }
  708. }
  709. else
  710. {
  711. if (inquotes) *out++=c;
  712. else if (neednull)
  713. {
  714. *out++=0;
  715. neednull=0;
  716. }
  717. }
  718. }
  719. *out++=0;
  720. *out++=0;
  721. }
  722. static int in_string(wchar_t *string, wchar_t *substring)
  723. {
  724. if (!string) return 0;
  725. if (!*substring) return 1;
  726. int l=lstrlen(substring);
  727. while (string[0]) if (!_wcsnicmp(string++,substring,l)) return 1;
  728. return 0;
  729. }
  730. C_ItemList * FilterSongs(const wchar_t * filter, const C_ItemList * songs, Device * dev, bool cloud)
  731. {
  732. wchar_t filterstr[256] = {0}, filteritems[300] = {0};
  733. lstrcpyn(filterstr,filter,256);
  734. parsequicksearch(filteritems,filterstr);
  735. C_ItemList * filtered = new C_ItemList;
  736. int l = songs->GetSize();
  737. for(int i=0; i<l; i++) {
  738. songid_t s = (songid_t)songs->Get(i);
  739. wchar_t *p=filteritems;
  740. if(p && *p) {
  741. while(p && *p) {
  742. bool in=false;
  743. for(int j=0; j<15; j++) {
  744. wchar_t buf[2048] = {0};
  745. int buflen=2048;
  746. if (cloud)
  747. {
  748. switch(j) {
  749. case 0: dev->getTrackArtist(s,buf,buflen); break;
  750. case 1: dev->getTrackTitle(s,buf,buflen); break;
  751. case 2: dev->getTrackAlbum(s,buf,buflen); break;
  752. case 3: { int l=dev->getTrackLength(s); wsprintf(buf,L"%d:%02d",l/1000/60,(l/1000)%60); break; }
  753. case 4: { dev->getTrackExtraInfo(s,L"cloud",buf,buflen); break; }
  754. case 5: wsprintf(buf,L"%d",dev->getTrackTrackNum(s)); break;
  755. case 6: wsprintf(buf,L"%d",dev->getTrackDiscNum(s)); break;
  756. case 7: dev->getTrackGenre(s,buf,buflen); break;
  757. case 8: wsprintf(buf,L"%d",dev->getTrackYear(s)); break;
  758. case 9: wsprintf(buf,WASABI_API_LNGSTRINGW(IDS_KBPS),dev->getTrackBitrate(s)); break;
  759. case 10: WASABI_API_LNG->FormattedSizeString(buf, buflen, dev->getTrackSize(s)); break;
  760. case 11: wsprintf(buf,L"%d",dev->getTrackPlayCount(s)); break;
  761. case 12: getStars(dev->getTrackRating(s),buf,buflen); break;
  762. case 13: timeToString(dev->getTrackLastPlayed(s),buf,buflen); break;
  763. case 14: dev->getTrackAlbumArtist(s,buf,buflen); break;
  764. case 15: dev->getTrackPublisher(s,buf,buflen); break;
  765. case 16: dev->getTrackComposer(s,buf,buflen); break;
  766. case 17: dev->getTrackMimeType(s,buf,buflen); break;
  767. case 18: timeToString(dev->getTrackDateAdded(s),buf,buflen); break;
  768. default: lstrcpyn(buf,L"",buflen); break;
  769. }
  770. }
  771. else
  772. {
  773. switch(j) {
  774. case 0: dev->getTrackArtist(s,buf,buflen); break;
  775. case 1: dev->getTrackTitle(s,buf,buflen); break;
  776. case 2: dev->getTrackAlbum(s,buf,buflen); break;
  777. case 3: { int l=dev->getTrackLength(s); wsprintf(buf,L"%d:%02d",l/1000/60,(l/1000)%60); break; }
  778. case 4: wsprintf(buf,L"%d",dev->getTrackTrackNum(s)); break;
  779. case 5: wsprintf(buf,L"%d",dev->getTrackDiscNum(s)); break;
  780. case 6: dev->getTrackGenre(s,buf,buflen); break;
  781. case 7: wsprintf(buf,L"%d",dev->getTrackYear(s)); break;
  782. case 8: wsprintf(buf,WASABI_API_LNGSTRINGW(IDS_KBPS),dev->getTrackBitrate(s)); break;
  783. case 9: WASABI_API_LNG->FormattedSizeString(buf, buflen, dev->getTrackSize(s)); break;
  784. case 10: wsprintf(buf,L"%d",dev->getTrackPlayCount(s)); break;
  785. case 11: getStars(dev->getTrackRating(s),buf,buflen); break;
  786. case 12: timeToString(dev->getTrackLastPlayed(s),buf,buflen); break;
  787. case 13: dev->getTrackAlbumArtist(s,buf,buflen); break;
  788. case 14: dev->getTrackPublisher(s,buf,buflen); break;
  789. case 15: dev->getTrackComposer(s,buf,buflen); break;
  790. case 16: dev->getTrackMimeType(s,buf,buflen); break;
  791. case 17: timeToString(dev->getTrackDateAdded(s),buf,buflen); break;
  792. default: lstrcpyn(buf,L"",buflen); break;
  793. }
  794. }
  795. if(in_string(buf,p)) { in=true; break;}
  796. }
  797. if(in) p+=lstrlen(p)+1;
  798. else break;
  799. }
  800. }
  801. if(p && *p) continue;
  802. filtered->Add((void*)s);
  803. }
  804. return filtered;
  805. }
  806. C_ItemList * ArtistAlbumLists::FilterSongs(const wchar_t * filter, const C_ItemList * songs)
  807. {
  808. return ::FilterSongs(filter,songs,dev,this->async);
  809. }
  810. static Filter * firstFil;
  811. static int sortFunc_ssi(const void *elem1, const void *elem2)
  812. {
  813. SortSongItem * a = (SortSongItem *)elem1;
  814. SortSongItem * b = (SortSongItem *)elem2;
  815. Filter * f = firstFil;
  816. while(f) {
  817. int r = f->sortFunc(a->dev,a->songid,b->songid);
  818. if(r) return r;
  819. f = f->nextFilter;
  820. }
  821. return 0;
  822. }
  823. C_ItemList * ArtistAlbumLists::CompilePrimaryList(const C_ItemList * songs) {
  824. C_ItemList * list = new C_ItemList;
  825. SortSongItem *songList = (SortSongItem*)calloc(songs->GetSize(), sizeof(SortSongItem));
  826. int l=songs->GetSize();
  827. for(int i=0; i<l; i++) { songList[i].songid = (songid_t)songs->Get(i); songList[i].dev = dev; }
  828. firstFil = firstFilter;
  829. qsort(songList,l,sizeof(SortSongItem),sortFunc_ssi); //sort it
  830. FilterItem * items[MAX_FILTERS]={0};
  831. Filter * filters[MAX_FILTERS]={0};
  832. Filter * f = firstFilter;
  833. int numFilters=0;
  834. while(f) {filters[numFilters++] = f; f=f->nextFilter;}
  835. for(int i=0; i<l; i++) {
  836. songid_t s = songList[i].songid;
  837. for(int j=0; j<numFilters; j++) {
  838. if(items[j] && filters[j]->isInGroup(dev,s,items[j])) filters[j]->addToGroup(dev,s,items[j]);
  839. else {
  840. for(int k=j; k<numFilters; k++) {
  841. items[k] = filters[k]->newGroup(dev,s);
  842. items[k]->nextFilter = new C_ItemList;
  843. if(k==0) list->Add(items[k]);
  844. else {
  845. items[k-1]->nextFilter->Add(items[k]);
  846. items[k-1]->numNextFilter++;
  847. }
  848. }
  849. break;
  850. }
  851. }
  852. }
  853. if (songList)
  854. {
  855. free(songList);
  856. songList = 0;
  857. }
  858. if(list->GetSize() && ((FilterItem*)list->Get(0))->isWithoutGroup()) {
  859. wsprintf(this->filters[0]->topString,WASABI_API_LNGSTRINGW(IDS_ALL_X_WITHOUT_X),
  860. list->GetSize()-1,(list->GetSize()==2)?this->filters[0]->filter->name:this->filters[0]->filter->namePlural,((FilterItem*)list->Get(0))->numTracks,this->filters[0]->filter->name);
  861. }
  862. else
  863. wsprintf(this->filters[0]->topString,WASABI_API_LNGSTRINGW(IDS_ALL_X),
  864. list->GetSize(),(list->GetSize()==1)?this->filters[0]->filter->name:this->filters[0]->filter->namePlural);
  865. CharLower(this->filters[0]->topString+3);
  866. this->filters[0]->tracks = songs->GetSize();
  867. return list;
  868. }
  869. static int sortFunc_filters(const void *elem1, const void *elem2) {
  870. FilterItem *a=(FilterItem *)*(void **)elem1;
  871. FilterItem *b=(FilterItem *)*(void **)elem2;
  872. return a->compareTo(b);
  873. }
  874. C_ItemList * ArtistAlbumLists::CompileSecondaryList(const C_ItemList * selectedItems, int level, bool updateTopArtist) {
  875. int totalTracks=0;
  876. C_ItemList * list = new C_ItemList;
  877. C_ItemList * collatedlist = new C_ItemList;
  878. for(int i=0; i < selectedItems->GetSize(); i++) {
  879. FilterItem * item = (FilterItem*)selectedItems->Get(i);
  880. if (item) {
  881. C_ItemList *nf = item->independentNextFilter?item->independentNextFilter:item->nextFilter;
  882. for(int j=0; j < nf->GetSize(); j++) list->Add(nf->Get(j));
  883. }
  884. }
  885. qsort(list->GetAll(),list->GetSize(),sizeof(void*),sortFunc_filters);
  886. FilterItem * curItem=0;
  887. for(int i=0; i < list->GetSize(); i++) {
  888. FilterItem * item = (FilterItem*)list->Get(i);
  889. if(curItem && !curItem->compareTo(item)) {
  890. curItem->independentTracks += item->numTracks;
  891. curItem->independentSize += item->size;
  892. curItem->independentLength += item->length;
  893. curItem->independentCloudState += item->cloudState;
  894. } else {
  895. curItem = item;
  896. collatedlist->Add(item);
  897. item->independentTracks = item->numTracks;
  898. curItem->independentSize = item->size;
  899. curItem->independentLength = item->length;
  900. curItem->independentCloudState = item->cloudState;
  901. if(item->independentNextFilter) delete item->independentNextFilter;
  902. item->independentNextFilter = new C_ItemList;
  903. }
  904. totalTracks+=item->numTracks;
  905. for(int k=0; k<item->nextFilter->GetSize(); k++) curItem->independentNextFilter->Add(item->nextFilter->Get(k));
  906. }
  907. delete list;
  908. if(updateTopArtist) this->filters[level-1]->nextFilterNum = collatedlist->GetSize();
  909. if(collatedlist->GetSize() && ((FilterItem*)collatedlist->Get(0))->isWithoutGroup()) {
  910. wsprintf(this->filters[level]->topString,WASABI_API_LNGSTRINGW(IDS_ALL_X_WITHOUT_X),
  911. collatedlist->GetSize(),(collatedlist->GetSize()==1)?this->filters[level]->filter->name:this->filters[level]->filter->namePlural,((FilterItem*)collatedlist->Get(0))->numTracks,this->filters[level]->filter->name);
  912. }
  913. else {
  914. wsprintf(this->filters[level]->topString,WASABI_API_LNGSTRINGW(IDS_ALL_X),
  915. collatedlist->GetSize(),(collatedlist->GetSize()==1)?this->filters[level]->filter->name:this->filters[level]->filter->namePlural);
  916. }
  917. CharLower(this->filters[level]->topString+3);
  918. this->filters[level]->tracks=totalTracks;
  919. return collatedlist;
  920. }
  921. // removes song from all relevant lists
  922. void ArtistAlbumLists::RemoveTrack(songid_t song)
  923. {
  924. if (searchedTracks)
  925. {
  926. for (int i=0;i<searchedTracks->GetSize();i++)
  927. {
  928. if (searchedTracks->Get(i) == (void *) song)
  929. {
  930. searchedTracks->Del(i--);
  931. }
  932. }
  933. }
  934. }
  935. void ArtistAlbumLists::SelectionChanged(int filterNum, SkinnedListView **listview) {
  936. for(int i=filterNum; i<numFilters-1; i++) {
  937. int l = listview[i]->listview.GetCount();
  938. bool all = (i != filterNum || (ListView_GetSelectedCount(listview[i]->listview.getwnd())==0));
  939. if(listview[i]->listview.GetSelected(0) && filters[i]->filter->HaveTopItem()) all=true;
  940. C_ItemList selectedItems;
  941. int j=0,f=0;
  942. if(filters[i]->filter->HaveTopItem()) { j=1; f=1; }
  943. for(; j<l; j++) {
  944. if(all || listview[i]->listview.GetSelected(j))
  945. selectedItems.Add(filters[i]->items->Get(j-f));
  946. }
  947. delete filters[i+1]->items;
  948. filters[i+1]->items = CompileSecondaryList(&selectedItems,i+1,false);
  949. filters[i+1]->SortList();
  950. listview[i+1]->UpdateList();
  951. }
  952. C_ItemList * tracks = new C_ItemList;
  953. C_ItemList * selectedItems[MAX_FILTERS]={0};
  954. for(int i=0; i<=filterNum; i++) {
  955. bool all = (ListView_GetSelectedCount(listview[i]->listview.getwnd())==0);
  956. if(listview[i]->listview.GetSelected(0) && filters[i]->filter->HaveTopItem()) all=true;
  957. if(all) selectedItems[i] = NULL;
  958. else {
  959. selectedItems[i] = new C_ItemList;
  960. int m = filters[i]->items->GetSize();
  961. int offset = filters[i]->filter->HaveTopItem()?1:0;
  962. for(int k=0; k<m; k++) if(listview[i]->listview.GetSelected(k+offset)) selectedItems[i]->Add(filters[i]->items->Get(k));
  963. }
  964. }
  965. int l=searchedTracks->GetSize();
  966. for(int j=0; j<l; j++) {
  967. songid_t track = (songid_t)searchedTracks->Get(j);
  968. bool matches=true;
  969. for(int i=0; i<=filterNum; i++) {
  970. matches=false;
  971. if(selectedItems[i]) {
  972. for(int k=0; k<selectedItems[i]->GetSize(); k++)
  973. if(filters[i]->filter->isInGroup(dev,track,(FilterItem*)selectedItems[i]->Get(k))) { matches=true; break; }
  974. }
  975. else matches=true;
  976. if(!matches) break;
  977. }
  978. if(matches) //woo hoo, its in!
  979. tracks->Add((void*)track);
  980. }
  981. delete unrefinedTracks;
  982. unrefinedTracks = tracks;
  983. SetRefine(L"");
  984. for(int i=0; i<=filterNum; i++)
  985. {
  986. delete selectedItems[i];
  987. }
  988. }
  989. void ArtistAlbumLists::SetRefine(const wchar_t * str, bool async) {
  990. if (!async)
  991. {
  992. C_ItemList * refinedTracks = FilterSongs(str,unrefinedTracks);
  993. C_ItemList * oldTrackList = trackList;
  994. trackList = refinedTracks;
  995. tracksLC->tracks = trackList;
  996. delete oldTrackList;
  997. tracksLC->SortList();
  998. // get stats
  999. __int64 fileSize=0;
  1000. int playLength=0;
  1001. int millis=0;
  1002. for(int i=0; i < trackList->GetSize(); i++) {
  1003. songid_t s = (songid_t)trackList->Get(i);
  1004. fileSize += (__int64)dev->getTrackSize(s);
  1005. playLength += dev->getTrackLength(s)/1000;
  1006. millis += dev->getTrackLength(s)%1000;
  1007. }
  1008. playLength += millis/1000;
  1009. tracksLC->totalPlayLength=playLength;
  1010. tracksLC->totalSize=fileSize;
  1011. }
  1012. else
  1013. {
  1014. if (lastRefine)
  1015. {
  1016. free(lastRefine);
  1017. lastRefine = 0;
  1018. }
  1019. lastRefine = wcsdup(str);
  1020. bgQuery(2);
  1021. }
  1022. }
  1023. void ArtistAlbumLists::SetSearch(const wchar_t * str, bool async) {
  1024. bgQuery_Stop();
  1025. if (!async)
  1026. {
  1027. delete searchedTracks; searchedTracks = NULL;
  1028. C_ItemList allTracks;
  1029. int l = dev->getPlaylistLength(playlistId);
  1030. for(int i=0; i<l; i++) {
  1031. songid_t x = dev->getPlaylistTrack(playlistId,i);
  1032. if(type != -1 && dev->getTrackType(x) != type) continue;
  1033. allTracks.Add((void*)x);
  1034. }
  1035. searchedTracks = FilterSongs(str,&allTracks);
  1036. delete unrefinedTracks;
  1037. unrefinedTracks = new C_ItemList;
  1038. l=searchedTracks->GetSize();
  1039. for(int i=0; i<l; i++) unrefinedTracks->Add(searchedTracks->Get(i));
  1040. for(int i=0; i<numFilters; i++) {
  1041. if(i==0) {
  1042. FreeFilterItemList(filters[0]->items);
  1043. filters[0]->items = CompilePrimaryList(searchedTracks);
  1044. }
  1045. else {
  1046. delete filters[i]->items;
  1047. filters[i]->items = CompileSecondaryList(filters[i-1]->items,i,true);
  1048. }
  1049. }
  1050. SetRefine(L"");
  1051. for(int i=0; i<numFilters; i++) filters[i]->SortList();
  1052. }
  1053. else
  1054. {
  1055. if (lastSearch)
  1056. {
  1057. free(lastSearch);
  1058. lastSearch = 0;
  1059. }
  1060. if (!(str && *str))
  1061. {
  1062. if (unrefinedTracks) delete unrefinedTracks;
  1063. if (searchedTracks) delete searchedTracks;
  1064. if (trackList) delete trackList;
  1065. searchedTracks = new C_ItemList;
  1066. trackList = new C_ItemList;
  1067. unrefinedTracks = new C_ItemList;
  1068. }
  1069. else
  1070. {
  1071. lastSearch = wcsdup(str);
  1072. }
  1073. bgQuery((str && *str));
  1074. }
  1075. }
  1076. ListContents * ArtistAlbumLists::GetFilterList(int i) { return filters[i]; }
  1077. PrimaryListContents * ArtistAlbumLists::GetTracksList() { return this->tracksLC; }