SaveQuery.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. #include "main.h"
  2. #include "ml_local.h"
  3. #include <shlwapi.h>
  4. #include <assert.h>
  5. #include "../nu/sort.h"
  6. /*
  7. #ifdef _M_IX86
  8. #undef min
  9. static inline int min(int x, int y)
  10. {
  11. return y+((x-y)>>31)&(x-y);
  12. }
  13. #undef max
  14. static inline int max(int x, int y)
  15. {
  16. return x-(((x-y)>>(31))&(x-y));
  17. }
  18. #elif defined(_WIN64)
  19. #undef min
  20. static inline int min(int x, int y)
  21. {
  22. return y+((x-y)>>63)&(x-y);
  23. }
  24. #undef max
  25. static inline int max(int x, int y)
  26. {
  27. return x-(((x-y)>>(63))&(x-y));
  28. }
  29. #endif
  30. */
  31. #ifdef _M_IX86
  32. const size_t convert_max_characters = 16; // it's actually 11, but this probably causes less memory fragmentation
  33. #elif defined(_M_X64)
  34. const size_t convert_max_characters = 20;
  35. #endif
  36. static inline int Compare_Int_NegativeIsNull(int v1, int v2)
  37. {
  38. v1 = max(v1,0);
  39. v2 = max(v2,0);
  40. return(v1 - v2);
  41. }
  42. typedef int (__fastcall *SortFunction)(const itemRecordW *a, const itemRecordW *b);
  43. #define SORT(field) SortBy ## field
  44. #define STRING_SORT(field) static int __fastcall SORT(field)(const itemRecordW *a, const itemRecordW *b) { return WCSCMP_NULLOK(a->field, b->field); }
  45. #define EXT_STRING_SORT(field) static int __fastcall SORT(field)(const itemRecordW *a, const itemRecordW *b) { wchar_t *a_field = getRecordExtendedItem_fast(a, extended_fields.field); wchar_t *b_field = getRecordExtendedItem_fast(b, extended_fields.field); return WCSCMP_NULLOK(a_field, b_field);}
  46. #define TIME_SORT(field) static int __fastcall SORT(field)(const itemRecordW *a, const itemRecordW *b) { time_t v1 = (time_t)a->field; time_t v2 = (time_t)b->field; if (v1 == -1) v1 = 0; if (v2 == -1) v2 = 0; return (int)(v1 - v2);}
  47. #define EXT_TIME_SORT(field) static int __fastcall SORT(field)(const itemRecordW *a, const itemRecordW *b) { wchar_t *a_field = getRecordExtendedItem_fast(a, extended_fields.dateadded); wchar_t *b_field = getRecordExtendedItem_fast(b, extended_fields.dateadded); time_t v1 = a_field?_wtoi(a_field):0; time_t v2 = b_field?_wtoi(b_field):0; if (v1 == -1) v1 = 0; if (v2 == -1) v2 = 0; return (int)(v1 - v2);}
  48. #define INT_SORT(field) static int __fastcall SORT(field)(const itemRecordW *a, const itemRecordW *b) { return Compare_Int_NegativeIsNull(a->field, b->field); }
  49. #define EXT_INT_SORT(field) static int __fastcall SORT(field)(const itemRecordW *a, const itemRecordW *b) { wchar_t *a_field = getRecordExtendedItem_fast(a, extended_fields.field); wchar_t *b_field = getRecordExtendedItem_fast(b, extended_fields.field); int v1 = a_field?_wtoi(a_field):0; int v2 = b_field?_wtoi(b_field):0; return (v1-v2);}
  50. #define FLOAT_SORT(field) static int __fastcall SORT(field)(const itemRecordW *a, const itemRecordW *b) { return FLOATWCMP_NULLOK(a->field, b->field); }
  51. STRING_SORT(artist);
  52. STRING_SORT(title);
  53. STRING_SORT(album);
  54. INT_SORT(length);
  55. INT_SORT(track);
  56. STRING_SORT(genre);
  57. INT_SORT(year);
  58. static int __fastcall SORT(filespec)(const itemRecordW *a, const itemRecordW *b)
  59. {
  60. // remove path before compare...
  61. wchar_t * af = L"";
  62. if (a->filename)
  63. af = PathFindFileNameW(a->filename);
  64. wchar_t * bf = L"";
  65. if (b->filename)
  66. bf = PathFindFileNameW(b->filename);
  67. return _wcsicmp(af, bf);
  68. }
  69. INT_SORT(rating);
  70. INT_SORT(playcount);
  71. TIME_SORT(lastplay);
  72. TIME_SORT(lastupd);
  73. TIME_SORT(filetime);
  74. STRING_SORT(comment);
  75. INT_SORT(filesize);
  76. INT_SORT(bitrate);
  77. INT_SORT(type);
  78. INT_SORT(disc);
  79. STRING_SORT(albumartist);
  80. STRING_SORT(filename);
  81. FLOAT_SORT(replaygain_album_gain);
  82. FLOAT_SORT(replaygain_track_gain);
  83. STRING_SORT(publisher);
  84. STRING_SORT(composer);
  85. static int __fastcall SORT(extension)(const itemRecordW *a, const itemRecordW *b)
  86. {
  87. wchar_t *extA = PathFindExtensionW(a->filename);
  88. wchar_t *extB = PathFindExtensionW(b->filename);
  89. if (extA && *extA)
  90. extA++;
  91. if (extB && *extB)
  92. extB++;
  93. return WCSCMP_NULLOK(extA, extB);
  94. }
  95. EXT_INT_SORT(ispodcast);
  96. EXT_STRING_SORT(podcastchannel);
  97. EXT_STRING_SORT(podcastpubdate);
  98. INT_SORT(bpm);
  99. STRING_SORT(category);
  100. EXT_STRING_SORT(director);
  101. EXT_STRING_SORT(producer);
  102. EXT_TIME_SORT(dateadded);
  103. EXT_STRING_SORT(cloud);
  104. static int __fastcall SORT(dimension)(const itemRecordW *a, const itemRecordW *b)
  105. {
  106. wchar_t *a_width = getRecordExtendedItem_fast(a, extended_fields.width);
  107. wchar_t *b_width = getRecordExtendedItem_fast(b, extended_fields.width);
  108. wchar_t *a_height = getRecordExtendedItem_fast(a, extended_fields.height);
  109. wchar_t *b_height = getRecordExtendedItem_fast(b, extended_fields.height);
  110. int w1 = a_width?_wtoi(a_width):0;
  111. int w2 = b_width?_wtoi(b_width):0;
  112. int h1 = a_height?_wtoi(a_height):0;
  113. int h2 = b_height?_wtoi(b_height):0;
  114. if (w1 != w2)
  115. return (w1-w2);
  116. else
  117. return (h1-h2);
  118. }
  119. // DISABLED FOR 5.62 RELEASE - DRO
  120. //EXT_STRING_SORT(codec);
  121. #define FORCE_ASCENDING ((SortFunction)-2)
  122. // this is where we define sort orders!
  123. static const SortFunction sortOrder[MEDIAVIEW_COL_NUMS][MEDIAVIEW_COL_NUMS+2] =
  124. {
  125. {SORT(artist), FORCE_ASCENDING, SORT(album), SORT(disc), SORT(track), SORT(title), 0}, // Artist
  126. {SORT(title), FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  127. {SORT(album), FORCE_ASCENDING, SORT(albumartist), SORT(disc), SORT(track), SORT(title), 0},
  128. {SORT(length), FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  129. {SORT(track), FORCE_ASCENDING, SORT(title), SORT(artist), SORT(album), SORT(disc), 0},
  130. {SORT(genre), FORCE_ASCENDING, SORT(albumartist), SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  131. {SORT(year), FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  132. {SORT(filespec), SORT(filename),0},
  133. {SORT(rating), SORT(playcount), SORT(lastplay), FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  134. {SORT(playcount), SORT(lastplay), FORCE_ASCENDING,SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  135. {SORT(lastplay), FORCE_ASCENDING,SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  136. {SORT(lastupd), FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  137. {SORT(filetime), FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  138. {SORT(comment), FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  139. {SORT(filesize),FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  140. {SORT(bitrate), FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  141. {SORT(type), FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  142. {SORT(disc), FORCE_ASCENDING, SORT(track), SORT(title), SORT(artist), SORT(album), SORT(disc), 0},
  143. {SORT(albumartist), FORCE_ASCENDING, SORT(album), SORT(disc), SORT(track), 0},
  144. {SORT(filename), 0},
  145. {SORT(replaygain_album_gain), FORCE_ASCENDING, SORT(album), SORT(disc), SORT(track), SORT(title), 0},
  146. {SORT(replaygain_track_gain), FORCE_ASCENDING, SORT(title), 0},
  147. {SORT(publisher),FORCE_ASCENDING,SORT(artist), SORT(album), SORT(disc), SORT(track), SORT(title), 0},
  148. {SORT(composer), FORCE_ASCENDING,SORT(album), SORT(disc), SORT(track), 0},
  149. {SORT(extension), FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  150. {SORT(ispodcast), FORCE_ASCENDING, SORT(podcastchannel), SORT(title), 0},
  151. {SORT(podcastchannel), SORT(title), 0},
  152. {SORT(podcastpubdate), 0},
  153. {SORT(bpm), 0}, // TODO
  154. {SORT(category),FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), SORT(title), 0},
  155. {SORT(director),FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), SORT(title), 0},
  156. {SORT(producer),FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), SORT(title), 0},
  157. {SORT(dimension), 0},
  158. // DISABLED FOR 5.62 RELEASE - DRO
  159. //{SORT(codec), 0},
  160. {SORT(dateadded), FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  161. {SORT(cloud), FORCE_ASCENDING, SORT(artist), SORT(album), SORT(disc), SORT(track), 0},
  162. };
  163. /* ---- */
  164. struct SortRules
  165. {
  166. int by;
  167. int dir;
  168. };
  169. static int __fastcall sortFuncW(const void *elem1, const void *elem2, const void *context)
  170. {
  171. assert(sizeof(sortOrder) / sizeof(*sortOrder) == MEDIAVIEW_COL_NUMS);
  172. const itemRecordW *a = (const itemRecordW*)elem1;
  173. const itemRecordW *b = (const itemRecordW*)elem2;
  174. const SortRules *rules = (SortRules *)context;
  175. int use_by = rules->by;
  176. int use_dir = !!rules->dir;
  177. int dir_values[] = {-1, 1};
  178. #define RETIFNZ(v) if ((v)<0) return dir_values[use_dir]; if ((v)>0) return -dir_values[use_dir];
  179. const SortFunction *order =sortOrder[use_by];
  180. int i=0;
  181. while (order[i])
  182. {
  183. if (order[i]==FORCE_ASCENDING)
  184. {
  185. use_dir=0;
  186. i++;
  187. continue;
  188. }
  189. int v = order[i](a, b);
  190. RETIFNZ(v);
  191. i++;
  192. }
  193. return 0;
  194. }
  195. void sortResults(int column, int dir, itemRecordListW *obj) // sorts the results based on the current sort mode
  196. {
  197. if (obj->Size > 1)
  198. {
  199. SortRules rules = {column, dir};
  200. qsort_itemRecord(obj->Items, obj->Size, &rules, sortFuncW);
  201. }
  202. }
  203. void sortResults(C_Config *viewconf, itemRecordListW *obj) // sorts the results based on the current sort mode
  204. {
  205. if (viewconf)
  206. {
  207. SortRules rules = {viewconf->ReadInt(L"mv_sort_by", MEDIAVIEW_COL_ARTIST), viewconf->ReadInt(L"mv_sort_dir", 0)};
  208. if (obj->Size > 1)
  209. qsort_itemRecord(obj->Items, obj->Size, &rules, sortFuncW);
  210. }
  211. }
  212. void setCloudValue(itemRecordW *item, const wchar_t* value)
  213. {
  214. wchar_t *temp = ndestring_malloc(convert_max_characters*sizeof(wchar_t));
  215. lstrcpynW(temp, value, convert_max_characters);
  216. setRecordExtendedItem(item, extended_fields.cloud, temp);
  217. ndestring_release(temp);
  218. }
  219. int saveQueryToListW(C_Config *viewconf, nde_scanner_t s, itemRecordListW *obj,
  220. CloudFiles *uploaded, CloudFiles *uploading,
  221. resultsniff_funcW cb, int user32, int *killswitch, __int64 *total_bytes)
  222. {
  223. __int64 total_kb = 0;
  224. emptyRecordList(obj);
  225. EnterCriticalSection(&g_db_cs);
  226. NDE_Scanner_First(s, killswitch);
  227. if (killswitch && *killswitch)
  228. {
  229. LeaveCriticalSection(&g_db_cs);
  230. return 0;
  231. }
  232. int r = 0;
  233. unsigned int total_length_s = 0;
  234. unsigned int uncert_cnt = 0;
  235. unsigned int cert_cnt = 0;
  236. int recordCount = NDE_Scanner_GetRecordsCount(s);
  237. allocRecordList(obj, recordCount);
  238. do
  239. {
  240. nde_field_t f = NDE_Scanner_GetFieldByID(s, MAINTABLE_ID_FILENAME);
  241. if (f)
  242. {
  243. allocRecordList(obj, obj->Size + 1);
  244. if (!obj->Alloc) break;
  245. obj->Items[obj->Size].filename = NDE_StringField_GetString(f);
  246. ndestring_retain(obj->Items[obj->Size].filename);
  247. total_kb += ScannerRefToObjCacheNFNW(s, obj, (cb == (resultsniff_funcW)-1 ? true : false));
  248. // look for any files known to the cloud so the icon can be set accordingly
  249. // TODO possibly need to do more here to better cope with transient states??
  250. if (uploaded && uploaded->size() > 0)
  251. {
  252. bool found = false;
  253. for(CloudFiles::iterator iter = uploaded->begin(); iter != uploaded->end(); iter++)
  254. {
  255. if (nde_wcsicmp_fn(obj->Items[obj->Size - 1].filename,(*iter)) == 0)
  256. {
  257. bool pending = false;
  258. found = true;
  259. // catches files being uploaded but not fully known to be in the cloud, etc
  260. if (uploading && uploading->size() > 0)
  261. {
  262. for(CloudFiles::iterator iter2 = uploading->begin(); iter2 != uploading->end(); iter2++)
  263. {
  264. if (nde_wcsicmp_fn(obj->Items[obj->Size - 1].filename,(*iter2)) == 0)
  265. {
  266. pending = true;
  267. setCloudValue(&obj->Items[obj->Size - 1], L"5");
  268. break;
  269. }
  270. }
  271. }
  272. if (!pending)
  273. {
  274. setCloudValue(&obj->Items[obj->Size - 1], L"0");
  275. }
  276. break;
  277. }
  278. }
  279. if (!found)
  280. {
  281. // catches files being uploaded but not fully known to be in the cloud, etc
  282. if (uploading && uploading->size() > 0)
  283. {
  284. for(CloudFiles::iterator iter = uploading->begin(); iter != uploading->end(); iter++)
  285. {
  286. if (nde_wcsicmp_fn(obj->Items[obj->Size - 1].filename,(*iter)) == 0)
  287. {
  288. found = true;
  289. setCloudValue(&obj->Items[obj->Size - 1], L"5");
  290. break;
  291. }
  292. }
  293. }
  294. if (!found)
  295. {
  296. setCloudValue(&obj->Items[obj->Size - 1], L"4");
  297. }
  298. }
  299. }
  300. int thisl = obj->Items[obj->Size - 1].length;
  301. if (thisl > 0)
  302. {
  303. total_length_s += thisl;
  304. cert_cnt++;
  305. }
  306. else
  307. {
  308. uncert_cnt++;
  309. }
  310. }
  311. r = NDE_Scanner_Next(s, killswitch);
  312. if (killswitch && *killswitch)
  313. {
  314. LeaveCriticalSection(&g_db_cs);
  315. return 0;
  316. }
  317. }
  318. while (r && !NDE_Scanner_EOF(s));
  319. if (((Table *)g_table)->HasErrors()) // TODO: don't use C++ NDE API
  320. {
  321. wchar_t *last_query = NULL;
  322. if (m_media_scanner)
  323. {
  324. const wchar_t *lq = NDE_Scanner_GetLastQuery(m_media_scanner);
  325. if (lq) last_query = _wcsdup(lq);
  326. NDE_Table_DestroyScanner(g_table, m_media_scanner);
  327. }
  328. NDE_Table_Compact(g_table);
  329. if (m_media_scanner)
  330. {
  331. m_media_scanner = NDE_Table_CreateScanner(g_table);
  332. if (last_query != NULL)
  333. {
  334. NDE_Scanner_Query(m_media_scanner, last_query);
  335. free(last_query);
  336. }
  337. }
  338. }
  339. LeaveCriticalSection(&g_db_cs);
  340. if (total_bytes)
  341. {
  342. // maintain compatibility as needed
  343. if (cb == (resultsniff_funcW)-1)
  344. *total_bytes = total_kb * 1024;
  345. else
  346. *total_bytes = total_kb;
  347. }
  348. compactRecordList(obj);
  349. if (cb && cb != (resultsniff_funcW)-1)
  350. {
  351. cb(obj->Items, obj->Size, user32, killswitch);
  352. }
  353. if (killswitch && *killswitch) return 0;
  354. sortResults(viewconf, obj);
  355. if (killswitch && *killswitch) return 0;
  356. if (uncert_cnt)
  357. {
  358. if (cert_cnt)
  359. {
  360. __int64 avg_len = (__int64)total_length_s / cert_cnt;
  361. total_length_s += (DWORD)(avg_len * uncert_cnt);
  362. }
  363. total_length_s |= (1 << 31);
  364. }
  365. return total_length_s;
  366. }