1
0

view_media.cpp 120 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051
  1. #include "main.h"
  2. #include "ml_local.h"
  3. #include <windowsx.h>
  4. #include "../nu/listview.h"
  5. #include "..\..\General\gen_ml/config.h"
  6. #include "resource.h"
  7. #include <time.h>
  8. #include "..\..\General\gen_ml/ml_ipc.h"
  9. #include "../ml_pmp/pmp.h"
  10. #include "..\..\General\gen_ml/gaystring.h"
  11. #include "../nde/nde.h"
  12. #include "../replicant/nu/AutoWide.h"
  13. #include "../replicant/nu/AutoChar.h"
  14. #include "..\..\General\gen_ml/ml_ipc_0313.h"
  15. #include <math.h>
  16. #include <shlwapi.h>
  17. #include <strsafe.h>
  18. #include "..\..\General\gen_ml/menufucker.h"
  19. #include "api_mldb.h"
  20. #include "../replicant/foundation/error.h"
  21. static wchar_t oldText[4096];
  22. static int IPC_LIBRARY_SENDTOMENU;
  23. static HINSTANCE cloud_hinst;
  24. const int ML_MSG_PDXS_STATUS = 0x1001;
  25. const int ML_MSG_PDXS_MIX = 0x1002;
  26. void RefreshMetadata(HWND parent);
  27. static HRGN g_rgnUpdate = NULL;
  28. static int offsetX, offsetY, customAllowed;
  29. int groupBtn = 1, enqueuedef = 0;
  30. static viewButtons view;
  31. HWND hwndSearchGlobal = 0;
  32. //timers
  33. #define TIMER_RATINGAUTOUNHOVER_ID 65520
  34. #define TIMER_RATINGAUTOUNHOVER_DELAY 200
  35. void FixAmps(wchar_t *str, size_t len)
  36. {
  37. size_t realSize = 0;
  38. size_t extra = 0;
  39. wchar_t *itr = str;
  40. while (itr && *itr)
  41. {
  42. if (itr && *itr == L'&')
  43. extra++;
  44. itr++;
  45. realSize++;
  46. }
  47. extra = min(len - (realSize + 1), extra);
  48. while (extra)
  49. {
  50. str[extra+realSize] = str[realSize];
  51. if (str[realSize] == L'&')
  52. {
  53. extra--;
  54. str[extra+realSize] = L'&';
  55. }
  56. realSize--;
  57. }
  58. }
  59. void MakeDateString(__time64_t convertTime, wchar_t *dest, size_t destlen)
  60. {
  61. SYSTEMTIME sysTime;
  62. tm *newtime = _localtime64(&convertTime);
  63. if (newtime)
  64. {
  65. sysTime.wYear = (WORD)(newtime->tm_year + 1900);
  66. sysTime.wMonth = (WORD)(newtime->tm_mon + 1);
  67. sysTime.wDayOfWeek = (WORD)newtime->tm_wday;
  68. sysTime.wDay = (WORD)newtime->tm_mday;
  69. sysTime.wHour = (WORD)newtime->tm_hour;
  70. sysTime.wMinute = (WORD)newtime->tm_min;
  71. sysTime.wSecond = (WORD)newtime->tm_sec;
  72. sysTime.wMilliseconds = 0;
  73. GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &sysTime, NULL, dest, destlen);
  74. size_t dateSize = lstrlenW(dest);
  75. dest += dateSize;
  76. destlen -= dateSize;
  77. if (destlen)
  78. {
  79. *dest++ = L' ';
  80. destlen--;
  81. }
  82. GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &sysTime, NULL, dest, destlen);
  83. //wcsftime(expire_time, 63, L"%b %d, %Y", _localtime64(&convertTime));
  84. }
  85. else
  86. dest[0] = 0;
  87. }
  88. #define MAINTABLE_ID_CLOUD (unsigned char)-1
  89. const unsigned char extra_idsW[] =
  90. {
  91. MAINTABLE_ID_ISPODCAST,
  92. MAINTABLE_ID_PODCASTCHANNEL,
  93. MAINTABLE_ID_PODCASTPUBDATE,
  94. MAINTABLE_ID_GRACENOTEFILEID,
  95. MAINTABLE_ID_GRACENOTEEXTDATA,
  96. MAINTABLE_ID_LOSSLESS,
  97. MAINTABLE_ID_CODEC,
  98. MAINTABLE_ID_DIRECTOR,
  99. MAINTABLE_ID_PRODUCER,
  100. MAINTABLE_ID_WIDTH,
  101. MAINTABLE_ID_HEIGHT,
  102. MAINTABLE_ID_MIMETYPE,
  103. 0,
  104. MAINTABLE_ID_DATEADDED,
  105. MAINTABLE_ID_CLOUD,
  106. };
  107. const ExtendedFields extended_fields =
  108. {
  109. L"ispodcast",
  110. L"podcastchannel",
  111. L"podcastpubdate",
  112. L"GracenoteFileID",
  113. L"GracenoteExtData",
  114. L"lossless",
  115. L"codec",
  116. L"director",
  117. L"producer",
  118. L"width",
  119. L"height",
  120. L"mime",
  121. L"realsize",
  122. L"dateadded",
  123. L"cloud",
  124. };
  125. const wchar_t *extra_strsW[] =
  126. {
  127. extended_fields.ispodcast,
  128. extended_fields.podcastchannel,
  129. extended_fields.podcastpubdate,
  130. extended_fields.GracenoteFileID,
  131. extended_fields.GracenoteExtData,
  132. extended_fields.lossless,
  133. extended_fields.codec,
  134. extended_fields.director,
  135. extended_fields.producer,
  136. extended_fields.width,
  137. extended_fields.height,
  138. extended_fields.mimetype,
  139. extended_fields.realsize,
  140. extended_fields.dateadded,
  141. extended_fields.cloud,
  142. };
  143. const int NUM_EXTRA_COLSW = sizeof(extra_idsW) / sizeof(*extra_idsW);
  144. bool isMixable(itemRecordW &song);
  145. static int predixisExist;
  146. static BOOL g_displaysearch = TRUE;
  147. static BOOL g_displaycontrols = TRUE;
  148. nde_scanner_t m_media_scanner = 0;
  149. W_ListView resultlist;
  150. static int resultSkin;
  151. void fileInfoDialogs(HWND hwndParent);
  152. void editInfo(HWND hwndParent);
  153. void customizeColumnsDialog(HWND hwndParent);
  154. static HWND m_hwnd;
  155. itemRecordListW itemCache;
  156. static int bgThread_Kill = 0;
  157. static HANDLE bgThread_Handle;
  158. static bool isMixablePresent = true;
  159. CloudFiles cloudFiles, cloudUploading;
  160. typedef struct
  161. {
  162. UINT column_id;
  163. char *config_name;
  164. WORD defWidth;
  165. WORD minWidth;
  166. WORD maxWidth;
  167. }
  168. headerColumn;
  169. #define UNLIMITED_WIDTH ((WORD)-1)
  170. #define COLUMN_DEFMINWIDTH 12
  171. #define COLUMN_DEFMAXWIDTH UNLIMITED_WIDTH
  172. #define MAX_COLUMN_ORDER (MEDIAVIEW_COL_NUMS+1)
  173. static headerColumn columnList[MAX_COLUMN_ORDER - 1] =
  174. {
  175. {IDS_ARTIST, "mv_col_artist", 140, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  176. {IDS_TITLE, "mv_col_title", 140, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  177. {IDS_ALBUM, "mv_col_album", 140, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  178. {IDS_LENGTH, "mv_col_length", 55, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  179. {IDS_TRACK_NUMBER, "mv_col_track", 55, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  180. {IDS_GENRE, "mv_col_genre", 75, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  181. {IDS_YEAR, "mv_col_year", 55, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  182. {IDS_FILENAME, "mv_col_fn", 140, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  183. {IDS_RATING, "mv_col_rating", 65, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  184. {IDS_PLAY_COUNT, "mv_col_playcount", 70, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  185. {IDS_PLAYED_LAST, "mv_col_lastplay", 115, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  186. {IDS_LAST_UPDATED, "mv_col_lastupd", 115, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  187. {IDS_FILE_TIME, "mv_col_filetime", 115, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  188. {IDS_COMMENT, "mv_col_comment", 140, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  189. {IDS_FILE_SIZE, "mb_col_filesize", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  190. {IDS_BITRATE, "mb_col_bitrate", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  191. {IDS_TYPE, "mb_col_type", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  192. {IDS_DISC, "mb_col_disc", 55, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  193. {IDS_ALBUM_ARTIST, "mb_col_albumartist", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  194. {IDS_FILE_PATH, "mb_col_filepath", 140, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  195. {IDS_ALBUM_GAIN, "mb_col_albumgain", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  196. {IDS_TRACK_GAIN, "mb_col_trackgain", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  197. {IDS_PUBLISHER, "mb_col_publisher", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  198. {IDS_COMPOSER, "mb_col_composer", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  199. {IDS_EXTENSION, "mb_col_extension", 55, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  200. {IDS_IS_PODCAST, "mb_col_ispodcast", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  201. {IDS_PODCAST_CHANNEL, "mb_col_podcastchannel", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  202. {IDS_PODCAST_PUBLISH_DATE, "mb_col_podcastpubdate", 115, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  203. {IDS_BPM, "mb_col_bpm", 55, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  204. {IDS_CATEGORY, "mb_col_category", 75, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  205. {IDS_DIRECTOR, "mb_col_director", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  206. {IDS_PRODUCER, "mb_col_producer", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  207. {IDS_DIMENSION, "mb_col_dimension", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  208. {IDS_DATE_ADDED, "mb_col_dateadded", 115, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  209. {IDS_CLOUD, "mv_col_cloud", 27, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
  210. };
  211. //default column order
  212. static signed char defColumnOrderCloud[MAX_COLUMN_ORDER] =
  213. {
  214. MEDIAVIEW_COL_ARTIST,
  215. MEDIAVIEW_COL_ALBUM,
  216. MEDIAVIEW_COL_TRACK,
  217. MEDIAVIEW_COL_CLOUD,
  218. MEDIAVIEW_COL_TITLE,
  219. MEDIAVIEW_COL_LENGTH,
  220. MEDIAVIEW_COL_GENRE,
  221. MEDIAVIEW_COL_RATING,
  222. MEDIAVIEW_COL_PLAYCOUNT,
  223. MEDIAVIEW_COL_LASTPLAY,
  224. MEDIAVIEW_COL_YEAR,
  225. -1
  226. };
  227. static signed char defColumnOrder[MAX_COLUMN_ORDER] =
  228. {
  229. MEDIAVIEW_COL_ARTIST,
  230. MEDIAVIEW_COL_ALBUM,
  231. MEDIAVIEW_COL_TRACK,
  232. MEDIAVIEW_COL_TITLE,
  233. MEDIAVIEW_COL_LENGTH,
  234. MEDIAVIEW_COL_GENRE,
  235. MEDIAVIEW_COL_RATING,
  236. MEDIAVIEW_COL_PLAYCOUNT,
  237. MEDIAVIEW_COL_LASTPLAY,
  238. MEDIAVIEW_COL_YEAR,
  239. -1
  240. };
  241. static signed char columnOrder[MAX_COLUMN_ORDER];
  242. int WCSCMP_NULLOK(const wchar_t *pa, const wchar_t *pb)
  243. {
  244. if (!pa) pa = L"";
  245. else SKIP_THE_AND_WHITESPACEW(pa)
  246. if (!pb) pb = L"";
  247. else SKIP_THE_AND_WHITESPACEW(pb)
  248. return CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE /*| NORM_IGNORENONSPACE*/, pa, -1, pb, -1) - 2;
  249. // return lstrcmpi(pa, pb);
  250. }
  251. int FLOATWCMP_NULLOK(const wchar_t *pa, const wchar_t *pb)
  252. {
  253. if (pa) SKIP_THE_AND_WHITESPACEW(pa)
  254. if (pb) SKIP_THE_AND_WHITESPACEW(pb)
  255. if ((!pa || !*pa) && (!pb || !*pb))
  256. return 0;
  257. if (!pa || !*pa)
  258. return 1;
  259. if (!pb || !*pb)
  260. return -1;
  261. _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
  262. float a = (float)_wtof_l(pa,C_locale);
  263. float b = (float)_wtof_l(pb,C_locale);
  264. if (a > b)
  265. return 1;
  266. else if (a < b)
  267. return -1;
  268. else
  269. return 0;
  270. }
  271. typedef struct
  272. {
  273. resultsniff_funcW cb;
  274. int user32;
  275. }
  276. bgThreadParms;
  277. static int bg_total_len_s;
  278. static __int64 bg_total_len_bytes;
  279. static LARGE_INTEGER querytime;
  280. static HMENU rate_hmenu = NULL;
  281. static HMENU cloud_hmenu = NULL;
  282. static HMENU sendto_hmenu = NULL;
  283. static librarySendToMenuStruct s;
  284. static RATINGCOLUMN ratingColumn;
  285. static WCHAR ratingBackText[128];
  286. #define IDC_LIST2HEADER 2001 // WM_INITDIALOG assign this is to the IDC_LIST2 header
  287. // internal messages
  288. #define IM_SYNCHEADERORDER (WM_USER + 0xFFF0)
  289. static DWORD WINAPI bgThreadQueryProc(void *tmp)
  290. {
  291. bgThreadParms *p = (bgThreadParms*)tmp;
  292. bg_total_len_s = 0;
  293. bg_total_len_bytes = 0;
  294. LARGE_INTEGER starttime, endtime;
  295. QueryPerformanceCounter(&starttime);
  296. bg_total_len_s = saveQueryToListW(g_view_metaconf, m_media_scanner, &itemCache,
  297. &cloudFiles, &cloudUploading, p->cb, p->user32,
  298. &bgThread_Kill, &bg_total_len_bytes);
  299. QueryPerformanceCounter(&endtime);
  300. querytime.QuadPart = endtime.QuadPart - starttime.QuadPart;
  301. if (!bgThread_Kill) PostMessage(m_hwnd, WM_APP + 3, 0x69, 0);
  302. return 0;
  303. }
  304. void bgQuery_Stop() // exported for other people to call since it is useful (eventually
  305. // we should have bgQuery pass the new query info along but I'll do that soon)
  306. {
  307. if (bgThread_Handle)
  308. {
  309. bgThread_Kill = 1;
  310. WaitForSingleObject(bgThread_Handle, INFINITE);
  311. CloseHandle(bgThread_Handle);
  312. bgThread_Handle = 0;
  313. }
  314. KillTimer(m_hwnd, 123);
  315. }
  316. static void bgQuery(resultsniff_funcW cb = 0, int user32 = 0) // only internal used
  317. {
  318. bgQuery_Stop();
  319. SetDlgItemTextW(m_hwnd, IDC_MEDIASTATUS, WASABI_API_LNGSTRINGW(IDS_SCANNING));
  320. StringCchCopyW(oldText, 4096, WASABI_API_LNGSTRINGW(IDS_SCANNING));
  321. SetTimer(m_hwnd, 123, 200, NULL);
  322. DWORD id;
  323. static bgThreadParms parms;
  324. parms.cb = cb;
  325. parms.user32 = user32;
  326. bgThread_Kill = 0;
  327. bgThread_Handle = CreateThread(NULL, 0, bgThreadQueryProc, (LPVOID) & parms, 0, &id);
  328. }
  329. // this thing does not produce a fully valid itemRecordList. be afraid.
  330. static void copyFilesToItemCacheW(itemRecordListW *obj)
  331. {
  332. if (bgThread_Handle) return ;
  333. int cnt = itemCache.Size;
  334. int i, l = cnt;
  335. cnt = 0;
  336. for (i = 0;i < l;i++)
  337. {
  338. if (resultlist.GetSelected(i)) cnt++;
  339. }
  340. obj->Alloc = obj->Size = 0;
  341. if (!cnt) return ;
  342. allocRecordList(obj, cnt, 0);
  343. if (!obj->Items)
  344. {
  345. obj->Size = obj->Alloc = 0;
  346. return ;
  347. }
  348. for (i = 0; i < itemCache.Size; i ++)
  349. {
  350. if (resultlist.GetSelected(i))
  351. {
  352. obj->Items[obj->Size++] = itemCache.Items[i];
  353. // makes sure that we are providing filesize in kb as
  354. // per spec even if we store it as __int64 internally
  355. obj->Items[obj->Size-1].filesize /= 1024;
  356. }
  357. }
  358. }
  359. void playFiles(int enqueue, int all)
  360. {
  361. if (bgThread_Handle) return ;
  362. int cnt = 0;
  363. int l = itemCache.Size;
  364. int foo_all = 0; // all but play the only selected
  365. int foo_selected = -1;
  366. if (!enqueue && !all && g_config->ReadInt(L"viewplaymode", 1))
  367. {
  368. int selcnt = 0;
  369. for (int i = 0;i < l;i++)
  370. {
  371. if (resultlist.GetSelected(i)) selcnt++;
  372. }
  373. if (selcnt == 1)
  374. {
  375. foo_all = -1;
  376. }
  377. }
  378. for (int i = 0;i < l;i++)
  379. {
  380. if (foo_all || all || resultlist.GetSelected(i))
  381. {
  382. if (foo_all && foo_selected < 0 && resultlist.GetSelected(i)) foo_selected = i;
  383. if (!cnt)
  384. {
  385. if (!enqueue) SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_DELETE);
  386. cnt++;
  387. }
  388. wchar_t title[2048] = {0};
  389. TAG_FMT_EXT(itemCache.Items[i].filename, itemrecordWTagFunc, ndeTagFuncFree, (void*)&itemCache.Items[i], title, 2048, 0);
  390. enqueueFileWithMetaStructW s;
  391. s.filename = itemCache.Items[i].filename;
  392. s.title = title;
  393. s.ext = NULL;
  394. s.length = itemCache.Items[i].length;
  395. #ifndef _DEBUG
  396. ndestring_retain(itemCache.Items[i].filename);
  397. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILEW_NDE);
  398. #else
  399. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILEW);
  400. #endif
  401. }
  402. }
  403. if (cnt)
  404. {
  405. if (foo_selected >= 0)
  406. {
  407. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, foo_selected, IPC_SETPLAYLISTPOS);
  408. SendMessage(plugin.hwndWinampParent, WM_COMMAND, 40047, 0); // stop button, literally
  409. SendMessage(plugin.hwndWinampParent, WM_COMMAND, 40045, 0); // play button, literally
  410. }
  411. else if (!enqueue) SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_STARTPLAY);
  412. }
  413. }
  414. // out can never be bigger than in+1
  415. static void parsequicksearch(wchar_t *out, wchar_t *in) // parses a list into a list of terms that we are searching for
  416. {
  417. int inquotes = 0, neednull = 0;
  418. while (in && *in)
  419. {
  420. wchar_t c = *in++;
  421. if (c != ' ' && c != '\t' && c != '\"')
  422. {
  423. neednull = 1;
  424. *out++ = c;
  425. }
  426. else if (c == '\"')
  427. {
  428. inquotes = !inquotes;
  429. if (!inquotes)
  430. {
  431. *out++ = 0;
  432. neednull = 0;
  433. }
  434. }
  435. else
  436. {
  437. if (inquotes) *out++ = c;
  438. else if (neednull)
  439. {
  440. *out++ = 0;
  441. neednull = 0;
  442. }
  443. }
  444. }
  445. *out++ = 0;
  446. *out++ = 0;
  447. }
  448. void makeQueryStringFromText(GayStringW *query, wchar_t *text, int nf)
  449. {
  450. int ispar = 0;
  451. if (query->Get()[0])
  452. {
  453. ispar = 1;
  454. query->Append(L"&(");
  455. }
  456. if (!_wcsnicmp(text, L"query:", 6))
  457. query->Append(text + 6); // copy the query as is
  458. else if (text[0] == L'?')
  459. query->Append(text + 1);
  460. else
  461. {
  462. int isAny = 0;
  463. if (*text == L'*' && text[1] == L' ')
  464. {
  465. isAny = 1;
  466. text += 2;
  467. }
  468. wchar_t tmpbuf[2048 + 32] = {0};
  469. parsequicksearch(tmpbuf, text);
  470. int x;
  471. wchar_t *fields[] =
  472. {
  473. L"filename",
  474. L"title",
  475. L"artist",
  476. L"album",
  477. L"genre",
  478. L"albumartist",
  479. L"publisher",
  480. L"composer",
  481. };
  482. wchar_t *p = tmpbuf;
  483. while (p && *p)
  484. {
  485. size_t lenp = wcslen(p);
  486. if (p == tmpbuf) query->Append(L"(");
  487. else if (isAny) query->Append(L")|(");
  488. else query->Append(L")&(");
  489. if (p[0] == L'<' && p[wcslen(p) - 1] == L'>' && wcslen(p) > 2)
  490. {
  491. wchar_t *op = p;
  492. while (op && *op)
  493. {
  494. if (*op == L'\'') *op = L'\"';
  495. op++;
  496. }
  497. p[lenp - 1] = 0; // remove >
  498. query->Append(p + 1);
  499. }
  500. else
  501. {
  502. for (x = 0; x < (int)min(sizeof(fields) / sizeof(fields[0]), nf); x ++)
  503. {
  504. wchar_t *field = fields[x];
  505. if (x) query->Append(L"|");
  506. query->Append(field);
  507. query->Append(L" HAS \"");
  508. GayStringW escaped;
  509. queryStrEscape(p, escaped);
  510. query->Append(escaped.Get());
  511. query->Append(L"\"");
  512. }
  513. }
  514. p += lenp + 1;
  515. }
  516. query->Append(L")");
  517. }
  518. if (ispar) query->Append(L")");
  519. }
  520. static void doQuery(HWND hwndDlg, wchar_t *text, int dobg = 1)
  521. {
  522. bgQuery_Stop();
  523. GayStringW query;
  524. if (text[0]) makeQueryStringFromText(&query, text);
  525. wchar_t *parent_query = NULL;
  526. extern wchar_t* m_query;
  527. parent_query = m_query;
  528. SendMessage(GetParent(hwndDlg), WM_APP + 2, 0, (LPARAM)&parent_query);
  529. GayStringW q;
  530. if (parent_query && parent_query[0])
  531. {
  532. q.Set(L"(");
  533. q.Append(parent_query);
  534. q.Append(L")");
  535. }
  536. if (query.Get() && query.Get()[0])
  537. {
  538. if (q.Get()[0])
  539. {
  540. q.Append(L" & (");
  541. q.Append(query.Get());
  542. q.Append(L")");
  543. }
  544. else q.Set(query.Get());
  545. }
  546. EnterCriticalSection(&g_db_cs);
  547. NDE_Scanner_Query(m_media_scanner, q.Get());
  548. LeaveCriticalSection(&g_db_cs);
  549. if (dobg) bgQuery();
  550. }
  551. static void RecycleSelectedItems()
  552. {
  553. int totalItems = resultlist.GetSelectedCount();
  554. if (!totalItems)
  555. return ;
  556. SHFILEOPSTRUCTW fileOp;
  557. fileOp.hwnd = m_hwnd;
  558. fileOp.wFunc = FO_DELETE;
  559. fileOp.pFrom = 0;
  560. fileOp.pTo = 0;
  561. fileOp.fFlags = SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_USES_RECYCLEBIN) ? FOF_ALLOWUNDO : 0;
  562. fileOp.fAnyOperationsAborted = 0;
  563. fileOp.hNameMappings = 0;
  564. fileOp.lpszProgressTitle = 0;
  565. EnterCriticalSection(&g_db_cs);
  566. nde_scanner_t s = NDE_Table_CreateScanner(g_table);
  567. int cchLen = totalItems * (MAX_PATH + 1) + 1;
  568. wchar_t *files = new wchar_t[cchLen]; // need room for each file name, null terminated. then have to null terminate the whole list
  569. if (files) // if malloc succeeded
  570. {
  571. wchar_t *curFile = files;
  572. for (int i = 0; i < itemCache.Size; i++)
  573. {
  574. if (resultlist.GetSelected(i))
  575. {
  576. if (NDE_Scanner_LocateNDEFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, itemCache.Items[i].filename))
  577. {
  578. StringCchCopyW(curFile, cchLen, itemCache.Items[i].filename);
  579. curFile += wcslen(itemCache.Items[i].filename) + 1;
  580. }
  581. }
  582. }
  583. if (curFile != files)
  584. {
  585. curFile[0] = 0; // null terminate
  586. fileOp.pFrom = files;
  587. fileOp.fAnyOperationsAborted = 0;
  588. if (SHFileOperationW(&fileOp))
  589. {
  590. wchar_t title[64] = {0};
  591. MessageBoxW(m_hwnd, WASABI_API_LNGSTRINGW(IDS_ERROR_DELETING_FILES),
  592. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,title,64), MB_OK);
  593. }
  594. else
  595. {
  596. // only remove items if deletion was allowed
  597. if (!fileOp.fAnyOperationsAborted)
  598. {
  599. for (int j = 0; j < itemCache.Size; j++)
  600. {
  601. if (resultlist.GetSelected(j))
  602. {
  603. if (NDE_Scanner_LocateNDEFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, itemCache.Items[j].filename))
  604. {
  605. // Wasabi callback event for pre remove
  606. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_PRE, (size_t)itemCache.Items[j].filename, 0);
  607. NDE_Scanner_Delete(s);
  608. NDE_Scanner_Post(s);
  609. g_table_dirty++;
  610. // Wasabi callback event for post remove
  611. // ToDo: (BigG) Move outside of critical section somehow
  612. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_POST, (size_t)itemCache.Items[j].filename, 0);
  613. }
  614. }
  615. }
  616. }
  617. }
  618. }
  619. delete [] files;
  620. }
  621. else // if malloc failed ... maybe because there's too many items.
  622. {
  623. files = new wchar_t[MAX_PATH + 1 + 1]; // double null termination
  624. if (!files) // if this malloc failed, just bail out
  625. {
  626. NDE_Table_DestroyScanner(g_table, s);
  627. LeaveCriticalSection(&g_db_cs);
  628. return ;
  629. }
  630. fileOp.pFrom = files;
  631. for (int i = 0;i < itemCache.Size;i++)
  632. {
  633. if (resultlist.GetSelected(i))
  634. {
  635. if (NDE_Scanner_LocateNDEFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, itemCache.Items[i].filename))
  636. {
  637. StringCchCopyW(files, MAX_PATH + 1 + 1, itemCache.Items[i].filename);
  638. files[wcslen(itemCache.Items[i].filename) + 1] = 0; // double null terminate
  639. fileOp.fAnyOperationsAborted = 0;
  640. if (SHFileOperationW(&fileOp))
  641. {
  642. wchar_t mes[4096] = {0};
  643. StringCchPrintfW(mes, 4096, WASABI_API_LNGSTRINGW(IDS_ERROR_DELETING_X), files);
  644. MessageBoxW(m_hwnd, mes, WASABI_API_LNGSTRINGW(IDS_ERROR), MB_OK);
  645. continue;
  646. }
  647. if (!fileOp.fAnyOperationsAborted)
  648. {
  649. // Wasabi callback event for pre remove
  650. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_PRE, (size_t)itemCache.Items[i].filename, 0);
  651. NDE_Scanner_Delete(s);
  652. NDE_Scanner_Post(s);
  653. g_table_dirty++;
  654. // Wasabi callback event for post remove
  655. // ToDo: (BigG) Move outside of critical section somehow
  656. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_POST, (size_t)itemCache.Items[i].filename, 0);
  657. }
  658. }
  659. }
  660. delete files;
  661. }
  662. }
  663. NDE_Table_DestroyScanner(g_table, s);
  664. if (g_table_dirty) NDE_Table_Sync(g_table);
  665. g_table_dirty = 0;
  666. LeaveCriticalSection(&g_db_cs);
  667. resultlist.Clear();
  668. emptyRecordList(&itemCache);
  669. // this might be gay, refreshing it completely (i.e. the cursor pos gets put back to normal, etc),
  670. // but really it is necessary for the view to be accurate.
  671. SendMessage(m_hwnd, WM_APP + 1, 0, 0); //refresh current view
  672. }
  673. static void removeSelectedItems(int physical)
  674. {
  675. if (physical)
  676. {
  677. RecycleSelectedItems();
  678. return ;
  679. }
  680. int hasdel = 0;
  681. EnterCriticalSection(&g_db_cs);
  682. nde_scanner_t s = NDE_Table_CreateScanner(g_table);
  683. for (int i = 0;i < itemCache.Size;i++)
  684. {
  685. if (resultlist.GetSelected(i))
  686. {
  687. if (NDE_Scanner_LocateNDEFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, itemCache.Items[i].filename))
  688. {
  689. wchar_t conf[32] = {0};
  690. if (!hasdel && MessageBoxW(m_hwnd, WASABI_API_LNGSTRINGW(IDS_SURE_YOU_WANT_TO_REMOVE_SELECTED_FROM_LIBRARY),
  691. WASABI_API_LNGSTRINGW_BUF(IDS_CONFIRMATION,conf,32), MB_YESNO | MB_ICONQUESTION) != IDYES)
  692. {
  693. NDE_Table_DestroyScanner(g_table, s);
  694. LeaveCriticalSection(&g_db_cs);
  695. return ;
  696. //FUCKO> need to eat the RETURN msg
  697. //MSG msg;
  698. //while(PeekMessage(&msg,m_hwnd,WM_COMMAND,WM_COMMAND,1));
  699. }
  700. if (!hasdel) // stop any background queries
  701. {
  702. bgQuery_Stop();
  703. }
  704. // Wasabi callback event for pre remove
  705. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_PRE, (size_t)itemCache.Items[i].filename, 0);
  706. hasdel = 1;
  707. NDE_Scanner_Delete(s);
  708. NDE_Scanner_Post(s);
  709. g_table_dirty++;
  710. // Wasabi callback event for post remove
  711. // ToDo: (BigG) Move this outside of the critical section
  712. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_POST, (size_t)itemCache.Items[i].filename, 0);
  713. }
  714. }
  715. }
  716. NDE_Table_DestroyScanner(g_table, s);
  717. if (g_table_dirty) NDE_Table_Sync(g_table);
  718. g_table_dirty = 0;
  719. LeaveCriticalSection(&g_db_cs);
  720. if (!hasdel) return ;
  721. resultlist.Clear();
  722. emptyRecordList(&itemCache);
  723. // this might be gay, refreshing it completely (i.e. the cursor pos gets put back to normal, etc),
  724. // but really it is necessary for the view to be accurate.
  725. SendMessage(m_hwnd, WM_APP + 1, 0, 0); //refresh current view
  726. }
  727. static void exploreItemFolder(HWND hwndDlg)
  728. {
  729. if (resultlist.GetSelectionMark() >= 0)
  730. {
  731. int l=resultlist.GetCount();
  732. for(int i=0;i<l;i++)
  733. {
  734. if (resultlist.GetSelected(i))
  735. {
  736. WASABI_API_EXPLORERFINDFILE->AddFile(itemCache.Items[i].filename);
  737. }
  738. }
  739. WASABI_API_EXPLORERFINDFILE->ShowFiles();
  740. }
  741. }
  742. static void removeDeadFiles(HWND hwndDlg)
  743. {
  744. Scan_RemoveFiles(hwndDlg);
  745. // this might be gay, refreshing it completely (i.e. the cursor pos gets put back to normal, etc),
  746. // but really it is necessary for the view to be accurate.
  747. SendMessage(m_hwnd, WM_APP + 1, 0, 0); //refresh current view
  748. }
  749. static WNDPROC search_oldWndProc;
  750. static DWORD WINAPI search_newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  751. {
  752. if (uMsg == WM_KEYDOWN && wParam == VK_DOWN)
  753. {
  754. PostMessageW(GetParent(hwndDlg), WM_NEXTDLGCTL, (WPARAM)resultlist.getwnd(), (LPARAM)TRUE);
  755. ListView_SetItemState(resultlist.getwnd(), 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
  756. }
  757. return CallWindowProcW(search_oldWndProc, hwndDlg, uMsg, wParam, lParam);
  758. }
  759. typedef struct _LAYOUT
  760. {
  761. INT id;
  762. HWND hwnd;
  763. INT x;
  764. INT y;
  765. INT cx;
  766. INT cy;
  767. DWORD flags;
  768. HRGN rgn;
  769. }
  770. LAYOUT, PLAYOUT;
  771. #define SETLAYOUTPOS(_layout, _x, _y, _cx, _cy) { _layout->x=_x; _layout->y=_y;_layout->cx=_cx;_layout->cy=_cy;_layout->rgn=NULL; }
  772. #define SETLAYOUTFLAGS(_layout, _r) \
  773. { \
  774. BOOL fVis; \
  775. fVis = (WS_VISIBLE & (LONG)GetWindowLongPtr(_layout->hwnd, GWL_STYLE)); \
  776. if (_layout->x == _r.left && _layout->y == _r.top) _layout->flags |= SWP_NOMOVE; \
  777. if (_layout->cx == (_r.right - _r.left) && _layout->cy == (_r.bottom - _r.top)) _layout->flags |= SWP_NOSIZE; \
  778. if ((SWP_HIDEWINDOW & _layout->flags) && !fVis) _layout->flags &= ~SWP_HIDEWINDOW; \
  779. if ((SWP_SHOWWINDOW & _layout->flags) && fVis) _layout->flags &= ~SWP_SHOWWINDOW; \
  780. }
  781. #define LAYOUTNEEEDUPDATE(_layout) ((SWP_NOMOVE | SWP_NOSIZE) != ((SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW | SWP_SHOWWINDOW) & _layout->flags))
  782. #define GROUP_MIN 0x1
  783. #define GROUP_MAX 0x3
  784. #define GROUP_SEARCH 0x1
  785. #define GROUP_STATUSBAR 0x2
  786. #define GROUP_MAIN 0x3
  787. static void LayoutWindows(HWND hwnd, BOOL fRedraw, BOOL fUpdateAll = FALSE)
  788. {
  789. static INT controls[] =
  790. {
  791. GROUP_SEARCH, IDC_SEARCHCAPTION, IDC_CLEAR, IDC_QUICKSEARCH,
  792. GROUP_STATUSBAR, IDC_BUTTON_PLAY, IDC_BUTTON_ENQUEUE, IDC_BUTTON_MIX, IDC_BUTTON_CREATEPLAYLIST, IDC_BUTTON_INFOTOGGLE, IDC_MIXABLE, IDC_MEDIASTATUS,
  793. GROUP_MAIN, IDC_LIST2
  794. };
  795. INT index;
  796. RECT rc, rg, ri;
  797. LAYOUT layout[sizeof(controls)/sizeof(controls[0])], *pl;
  798. BOOL skipgroup;
  799. HRGN rgn = NULL;
  800. GetClientRect(hwnd, &rc);
  801. if (rc.bottom == rc.top || rc.right == rc.left) return;
  802. SetRect(&rg, rc.left, rc.top, rc.right, rc.bottom);
  803. pl = layout;
  804. skipgroup = FALSE;
  805. InvalidateRect(hwnd, NULL, TRUE);
  806. for (index = 0; index < sizeof(controls) / sizeof(*controls); index++)
  807. {
  808. if (controls[index] >= GROUP_MIN && controls[index] <= GROUP_MAX) // group id
  809. {
  810. skipgroup = FALSE;
  811. switch (controls[index])
  812. {
  813. case GROUP_SEARCH:
  814. if (g_displaysearch)
  815. {
  816. wchar_t buffer[128] = {0};
  817. HWND ctrl = GetDlgItem(hwnd, IDC_CLEAR);
  818. GetWindowTextW(ctrl, buffer, ARRAYSIZE(buffer));
  819. LRESULT idealSize = MLSkinnedButton_GetIdealSize(ctrl, buffer);
  820. SetRect(&rg, rc.left,
  821. rc.top + WASABI_API_APP->getScaleY(2),
  822. rc.right - WASABI_API_APP->getScaleX(2),
  823. rc.top + WASABI_API_APP->getScaleY(HIWORD(idealSize)+1));
  824. rc.top = rg.bottom + WASABI_API_APP->getScaleY(3);
  825. }
  826. skipgroup = !g_displaysearch;
  827. break;
  828. case GROUP_STATUSBAR:
  829. if (g_displaycontrols)
  830. {
  831. wchar_t buffer[128] = {0};
  832. HWND ctrl = GetDlgItem(hwnd, IDC_BUTTON_PLAY);
  833. GetWindowTextW(ctrl, buffer, ARRAYSIZE(buffer));
  834. LRESULT idealSize = MLSkinnedButton_GetIdealSize(ctrl, buffer);
  835. SetRect(&rg, rc.left + WASABI_API_APP->getScaleX(1),
  836. rc.bottom - WASABI_API_APP->getScaleY(HIWORD(idealSize)),
  837. rc.right, rc.bottom);
  838. rc.bottom = rg.top - WASABI_API_APP->getScaleY(3);
  839. }
  840. skipgroup = !g_displaycontrols;
  841. break;
  842. case GROUP_MAIN:
  843. SetRect(&rg, rc.left + WASABI_API_APP->getScaleX(1), rc.top, rc.right, rc.bottom);
  844. break;
  845. }
  846. continue;
  847. }
  848. if (skipgroup) continue;
  849. pl->id = controls[index];
  850. pl->hwnd = GetDlgItem(hwnd, pl->id);
  851. if (!pl->hwnd) continue;
  852. GetWindowRect(pl->hwnd, &ri);
  853. MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT)&ri, 2);
  854. pl->flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS;
  855. switch (pl->id)
  856. {
  857. case IDC_SEARCHCAPTION:
  858. {
  859. wchar_t buffer[128] = {0};
  860. GetWindowTextW(pl->hwnd, buffer, ARRAYSIZE(buffer));
  861. LRESULT idealSize = MLSkinnedStatic_GetIdealSize(pl->hwnd, buffer);
  862. SETLAYOUTPOS(pl, rg.left + WASABI_API_APP->getScaleX(2),
  863. rg.top + WASABI_API_APP->getScaleY(1),
  864. WASABI_API_APP->getScaleX(LOWORD(idealSize)),
  865. (rg.bottom - rg.top));
  866. rg.left += (pl->cx + WASABI_API_APP->getScaleX(4));
  867. break;
  868. }
  869. case IDC_CLEAR:
  870. {
  871. wchar_t buffer[128] = {0};
  872. GetWindowTextW(pl->hwnd, buffer, ARRAYSIZE(buffer));
  873. LRESULT idealSize = MLSkinnedButton_GetIdealSize(pl->hwnd, buffer);
  874. LONG width = LOWORD(idealSize) + WASABI_API_APP->getScaleX(6);
  875. pl->flags |= (((rg.right - rg.left) - width) > WASABI_API_APP->getScaleX(40)) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW ;
  876. SETLAYOUTPOS(pl, rg.right - width, rg.top, width, rg.bottom - rg.top);
  877. if (SWP_SHOWWINDOW & pl->flags) rg.right -= (pl->cx + WASABI_API_APP->getScaleX(4));
  878. break;
  879. }
  880. case IDC_QUICKSEARCH:
  881. pl->flags |= (rg.right > rg.left) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW;
  882. SETLAYOUTPOS(pl, rg.left, rg.top, rg.right - rg.left - WASABI_API_APP->getScaleX(1),
  883. (rg.bottom - rg.top) - WASABI_API_APP->getScaleY(1));
  884. break;
  885. case IDC_BUTTON_PLAY:
  886. case IDC_BUTTON_ENQUEUE:
  887. case IDC_BUTTON_MIX:
  888. case IDC_BUTTON_CREATEPLAYLIST:
  889. if (IDC_BUTTON_MIX != pl->id || customAllowed)
  890. {
  891. if (groupBtn && pl->id == IDC_BUTTON_PLAY && enqueuedef == 1)
  892. {
  893. pl->flags |= SWP_HIDEWINDOW;
  894. break;
  895. }
  896. if (groupBtn && pl->id == IDC_BUTTON_ENQUEUE && enqueuedef != 1)
  897. {
  898. pl->flags |= SWP_HIDEWINDOW;
  899. break;
  900. }
  901. if (groupBtn && (pl->id == IDC_BUTTON_PLAY || pl->id == IDC_BUTTON_ENQUEUE) && customAllowed)
  902. {
  903. pl->flags |= SWP_HIDEWINDOW;
  904. break;
  905. }
  906. if (pl->id == IDC_BUTTON_CREATEPLAYLIST && !AGAVE_API_PLAYLIST_GENERATOR)
  907. {
  908. pl->flags |= SWP_HIDEWINDOW;
  909. break;
  910. }
  911. wchar_t buffer[128] = {0};
  912. GetWindowTextW(pl->hwnd, buffer, ARRAYSIZE(buffer));
  913. LRESULT idealSize = MLSkinnedButton_GetIdealSize(pl->hwnd, buffer);
  914. LONG width = LOWORD(idealSize) + WASABI_API_APP->getScaleX(6);
  915. SETLAYOUTPOS(pl, rg.left, rg.bottom - WASABI_API_APP->getScaleY(HIWORD(idealSize)),
  916. width, WASABI_API_APP->getScaleY(HIWORD(idealSize)));
  917. pl->flags |= ((rg.right - rg.left) > width) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW;
  918. if (SWP_SHOWWINDOW & pl->flags) rg.left += (pl->cx + WASABI_API_APP->getScaleX(4));
  919. }
  920. else
  921. pl->flags |= SWP_HIDEWINDOW;
  922. break;
  923. case IDC_BUTTON_INFOTOGGLE:
  924. switch (SendMessage(GetParent(hwnd), WM_USER + 66, 0, 0))
  925. {
  926. case 0xFF:
  927. case 0xF0:
  928. {
  929. wchar_t buffer[128] = {0};
  930. GetWindowTextW(pl->hwnd, buffer, ARRAYSIZE(buffer));
  931. LRESULT idealSize = MLSkinnedButton_GetIdealSize(pl->hwnd, buffer);
  932. LONG width = LOWORD(idealSize) + WASABI_API_APP->getScaleX(6);
  933. pl->flags |= (((rg.right - rg.left) - (ri.right - ri.left)) > WASABI_API_APP->getScaleX(60)) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW ;
  934. SETLAYOUTPOS(pl, rg.right - width - WASABI_API_APP->getScaleX(2),
  935. rg.top, width, (rg.bottom - rg.top));
  936. if (SWP_SHOWWINDOW & pl->flags) rg.right -= (pl->cx + WASABI_API_APP->getScaleX(4));
  937. break;
  938. }
  939. }
  940. break;
  941. case IDC_MIXABLE:
  942. if (predixisExist & 1)
  943. {
  944. SETLAYOUTPOS(pl, rg.right - (ri.right - ri.left), rg.top, (ri.right - ri.left), (rg.bottom - rg.top));
  945. pl->flags |= ((rg.right - rg.left) - (ri.right - ri.left) > WASABI_API_APP->getScaleX(60)) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW;
  946. if (SWP_SHOWWINDOW & pl->flags) rg.right -= (pl->cx + WASABI_API_APP->getScaleX(4));
  947. }
  948. break;
  949. case IDC_MEDIASTATUS:
  950. SETLAYOUTPOS(pl, rg.left, rg.top, rg.right - rg.left, (rg.bottom - rg.top));
  951. pl->flags |= (pl->cx > WASABI_API_APP->getScaleX(16)) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW;
  952. break;
  953. case IDC_LIST2:
  954. SETLAYOUTPOS(pl, rg.left, rg.top + 1, (rg.right - rg.left) - WASABI_API_APP->getScaleX(3), (rg.bottom - rg.top) - WASABI_API_APP->getScaleY(2));
  955. break;
  956. }
  957. SETLAYOUTFLAGS(pl, ri);
  958. if (LAYOUTNEEEDUPDATE(pl))
  959. {
  960. if (SWP_NOSIZE == ((SWP_HIDEWINDOW | SWP_SHOWWINDOW | SWP_NOSIZE) & pl->flags) &&
  961. ri.left == (pl->x + offsetX) && ri.top == (pl->y + offsetY) && !fUpdateAll && IsWindowVisible(pl->hwnd))
  962. {
  963. SetRect(&ri, pl->x, pl->y, pl->cx + pl->x, pl->y + pl->cy);
  964. ValidateRect(hwnd, &ri);
  965. }
  966. pl++;
  967. }
  968. else if (!fUpdateAll && (fRedraw || (!offsetX && !offsetY)) && IsWindowVisible(pl->hwnd))
  969. {
  970. ValidateRect(hwnd, &ri);
  971. if (GetUpdateRect(pl->hwnd, NULL, FALSE))
  972. {
  973. if (!rgn) rgn = CreateRectRgn(0,0,0,0);
  974. GetUpdateRgn(pl->hwnd, rgn, FALSE);
  975. OffsetRgn(rgn, pl->x, pl->y);
  976. InvalidateRgn(hwnd, rgn, FALSE);
  977. }
  978. }
  979. }
  980. if (pl != layout)
  981. {
  982. LAYOUT *pc;
  983. HDWP hdwp = BeginDeferWindowPos((INT)(pl - layout));
  984. for (pc = layout; pc < pl && hdwp; pc++)
  985. {
  986. hdwp = DeferWindowPos(hdwp, pc->hwnd, NULL, pc->x, pc->y, pc->cx, pc->cy, pc->flags);
  987. }
  988. if (hdwp) EndDeferWindowPos(hdwp);
  989. if (!rgn) rgn = CreateRectRgn(0, 0, 0, 0);
  990. if (fRedraw)
  991. {
  992. GetUpdateRgn(hwnd, rgn, FALSE);
  993. for (pc = layout; pc < pl && hdwp; pc++)
  994. {
  995. if (pc->rgn)
  996. {
  997. OffsetRgn(pc->rgn, pc->x, pc->y);
  998. CombineRgn(rgn, rgn, pc->rgn, RGN_OR);
  999. }
  1000. }
  1001. RedrawWindow(hwnd, NULL, rgn, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASENOW | RDW_ALLCHILDREN);
  1002. }
  1003. if (g_rgnUpdate)
  1004. {
  1005. GetUpdateRgn(hwnd, g_rgnUpdate, FALSE);
  1006. for (pc = layout; pc < pl && hdwp; pc++)
  1007. {
  1008. if (pc->rgn)
  1009. {
  1010. OffsetRgn(pc->rgn, pc->x, pc->y);
  1011. CombineRgn(g_rgnUpdate, g_rgnUpdate, pc->rgn, RGN_OR);
  1012. }
  1013. }
  1014. }
  1015. for (pc = layout; pc < pl && hdwp; pc++)
  1016. if (pc->rgn) DeleteObject(pc->rgn);
  1017. }
  1018. if (rgn) DeleteObject(rgn);
  1019. ValidateRgn(hwnd, NULL);
  1020. }
  1021. static void updateInfoText(HWND hwndDlg, bool x = false)
  1022. {
  1023. int a = SendMessage(GetParent(hwndDlg), WM_USER + 66, x ? -1 : 0, 0);
  1024. if (a == 0xff)
  1025. SetDlgItemTextW(hwndDlg, IDC_BUTTON_INFOTOGGLE, WASABI_API_LNGSTRINGW(IDS_HIDE_INFO));
  1026. else if (a == 0xf0)
  1027. SetDlgItemTextW(hwndDlg, IDC_BUTTON_INFOTOGGLE, WASABI_API_LNGSTRINGW(IDS_SHOW_INFO));
  1028. else ShowWindow(GetDlgItem(hwndDlg, IDC_BUTTON_INFOTOGGLE), SW_HIDE);
  1029. }
  1030. static void initColumnsHeader(HWND hwndList)
  1031. {
  1032. INT index, sortby;
  1033. LVCOLUMNW lvc = {0, };
  1034. if (!hwndList || !IsWindow(hwndList)) return;
  1035. SendMessageW(hwndList, WM_SETREDRAW, FALSE, 0L);
  1036. while (SendMessageW(hwndList, LVM_DELETECOLUMN, 0, 0L));
  1037. sortby = g_view_metaconf->ReadInt(L"mv_sort_by", 1);
  1038. lvc.mask = LVCF_TEXT | LVCF_WIDTH;
  1039. lvc.pszText = L"";
  1040. lvc.cx = 30;
  1041. SendMessageW(hwndList, LVM_INSERTCOLUMNW, 0, (LPARAM)&lvc); // create dummy column
  1042. // TODO set to a zero width if not available
  1043. MLSkinnedHeader_SetCloudColumn(ListView_GetHeader(hwndList), -1); // reset the cloud status column so it'll be correctly removed
  1044. SetPropW(hwndList, L"pmp_list_info", (HANDLE)-1);
  1045. for (index = 0; columnOrder[index] != -1; index++)
  1046. {
  1047. headerColumn *cl = &columnList[columnOrder[index]];
  1048. lvc.pszText = WASABI_API_LNGSTRINGW(cl->column_id);
  1049. lvc.cx = g_view_metaconf->ReadInt(AutoWide(cl->config_name), cl->defWidth);
  1050. if (lvc.cx < cl->minWidth) lvc.cx = cl->minWidth;
  1051. // update position of the cloud column icon
  1052. if (columnOrder[index] == MEDIAVIEW_COL_CLOUD)
  1053. {
  1054. if (!cloud_hinst || cloud_hinst == (HINSTANCE)1 ||
  1055. !SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE))
  1056. {
  1057. MLSkinnedHeader_SetCloudColumn(ListView_GetHeader(hwndList), -1);
  1058. SetPropW(hwndList, L"pmp_list_info", (HANDLE)-1);
  1059. lvc.cx = 0;
  1060. }
  1061. else
  1062. {
  1063. MLSkinnedHeader_SetCloudColumn(ListView_GetHeader(hwndList), index);
  1064. SetPropW(hwndList, L"pmp_list_info", (HANDLE)index);
  1065. lvc.cx = 27;
  1066. MLCloudColumn_GetWidth(plugin.hwndLibraryParent, &lvc.cx);
  1067. }
  1068. }
  1069. SendMessageW(hwndList, LVM_INSERTCOLUMNW, 0xFFFF, (LPARAM)&lvc);
  1070. if (sortby == columnOrder[index]) MLSkinnedListView_DisplaySort(hwndList, index, !g_view_metaconf->ReadInt(L"mv_sort_dir", 0));
  1071. }
  1072. SendMessageW(hwndList, LVM_DELETECOLUMN, 0, 0L); // Delete dummy column
  1073. for (index = 0; -1 != columnOrder[index] && MEDIAVIEW_COL_RATING != columnOrder[index]/* && MEDIAVIEW_COL_CLOUD != columnOrder[index]*/; index++);
  1074. if (-1 != index) SendMessageW(hwndList, LVM_SETCOLUMNWIDTH, index, (LPARAM)SendMessageW(hwndList, LVM_GETCOLUMNWIDTH, index, 0L));
  1075. SendMessageW(hwndList, WM_SETREDRAW, TRUE, 0L);
  1076. }
  1077. static int m_last_selitem = -1;
  1078. static int m_bgupdinfoviewerflag;
  1079. extern void add_to_library(HWND wndparent);
  1080. static INT_PTR CALLBACK needAddFilesProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1081. {
  1082. switch (uMsg)
  1083. {
  1084. case WM_INITDIALOG:
  1085. if (AGAVE_API_ITUNES_IMPORTER && AGAVE_API_ITUNES_IMPORTER->iTunesExists())
  1086. EnableWindow(GetDlgItem(hwndDlg, IDC_IMPORT_ITUNES), TRUE);
  1087. else
  1088. EnableWindow(GetDlgItem(hwndDlg, IDC_IMPORT_ITUNES), FALSE);
  1089. SetTimer(hwndDlg, 1, 1000, NULL);
  1090. return 1;
  1091. case WM_COMMAND:
  1092. switch(LOWORD(wParam))
  1093. {
  1094. case IDOK:
  1095. case IDCANCEL:
  1096. if (BN_CLICKED == HIWORD(wParam))
  1097. {
  1098. if (IsDlgButtonChecked(hwndDlg, IDC_CHECK1)) g_config->WriteInt(L"noshowadddlg", 1);
  1099. EndDialog(hwndDlg, 0);
  1100. }
  1101. break;
  1102. case ID_ADD_FILES:
  1103. if (BN_CLICKED == HIWORD(wParam))
  1104. {
  1105. add_to_library(hwndDlg);
  1106. PostMessage(hwndDlg, WM_TIMER, 1, 0);
  1107. }
  1108. break;
  1109. case IDC_IMPORT_ITUNES:
  1110. if (BN_CLICKED == HIWORD(wParam))
  1111. {
  1112. if (AGAVE_API_ITUNES_IMPORTER)
  1113. AGAVE_API_ITUNES_IMPORTER->ImportFromiTunes(hwndDlg);
  1114. PostMessage(hwndDlg, WM_TIMER, 1, 0);
  1115. if (m_curview_hwnd) PostMessage(m_curview_hwnd, WM_APP + 1, 0, 0); //update current view
  1116. }
  1117. break;
  1118. case IDC_BTN_LINK_PROMO:
  1119. if (BN_CLICKED == HIWORD(wParam)) ShellExecuteA(plugin.hwndWinampParent, "open", "https://help.winamp.com/hc/articles/8105244490772-Player-Overview", NULL, ".", 0);
  1120. break;
  1121. }
  1122. break;
  1123. case WM_TIMER:
  1124. if (g_table && NDE_Table_GetRecordsCount(g_table))
  1125. {
  1126. wchar_t buf[512] = {0};
  1127. StringCchPrintfW(buf, 512, WASABI_API_LNGSTRINGW(IDS_THERE_ARE_NOW_X_ITEMS_IN_THE_LIBRARY), NDE_Table_GetRecordsCount(g_table));
  1128. SetDlgItemTextW(hwndDlg, IDC_TEXT, buf);
  1129. SetDlgItemTextW(hwndDlg, ID_ADD_FILES, WASABI_API_LNGSTRINGW(IDS_ADD_MORE));
  1130. }
  1131. break;
  1132. case WM_DRAWITEM:
  1133. {
  1134. DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT *)lParam;
  1135. if (di->CtlType == ODT_BUTTON)
  1136. {
  1137. wchar_t wt[123] = {0};
  1138. int y;
  1139. RECT r;
  1140. HPEN hPen, hOldPen;
  1141. DWORD style;
  1142. GetDlgItemText(hwndDlg, (INT)wParam, wt, ARRAYSIZE(wt));
  1143. style = (DWORD)GetWindowLongPtrW(di->hwndItem, GWL_STYLE);
  1144. // draw text
  1145. SetTextColor(di->hDC, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220));
  1146. memset(&r, 0, sizeof(r));
  1147. DrawText(di->hDC, wt, -1, &r, DT_SINGLELINE | DT_CALCRECT);
  1148. if (BS_RIGHT & style) r.left = max(di->rcItem.left+ 2, di->rcItem.right - r.right - 2);
  1149. else if (BS_LEFT & style) r.left = di->rcItem.left+ 2;
  1150. else r.left = ((di->rcItem.right - di->rcItem.left - 4) - r.right) / 2;
  1151. if (r.left < di->rcItem.left + 2)
  1152. {
  1153. r.left = di->rcItem.left + 2;
  1154. r.right = di->rcItem.right - 2;
  1155. }
  1156. else r.right += r.left;
  1157. if (r.right > di->rcItem.right - 2) r.right = di->rcItem.right - 2;
  1158. if (BS_TOP & style) r.top = di->rcItem.top;
  1159. else if(BS_VCENTER & style) r.top = ((di->rcItem.bottom - di->rcItem.top - 2) - r.bottom) /2;
  1160. else r.top = di->rcItem.bottom - 2 - r.bottom;
  1161. if (r.top < di->rcItem.top)
  1162. {
  1163. r.top = di->rcItem.top;
  1164. r.bottom = di->rcItem.bottom - 2;
  1165. }
  1166. else r.bottom += r.top;
  1167. if (r.bottom > di->rcItem.bottom - 2) r.bottom = di->rcItem.bottom - 2;
  1168. DrawText(di->hDC, wt, -1, &r, DT_SINGLELINE | DT_WORD_ELLIPSIS);
  1169. // draw underline
  1170. y = min(di->rcItem.bottom, r.bottom + 1);
  1171. hPen = CreatePen(PS_SOLID, 0, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220));
  1172. hOldPen = (HPEN) SelectObject(di->hDC, hPen);
  1173. MoveToEx(di->hDC, r.left, y, NULL);
  1174. LineTo(di->hDC, r.right, y);
  1175. SelectObject(di->hDC, hOldPen);
  1176. DeleteObject(hPen);
  1177. }
  1178. }
  1179. }
  1180. return 0;
  1181. };
  1182. static void SetStatusText(HWND hwndStatus, LPCWSTR *ppsz, INT count)
  1183. {
  1184. WCHAR buffer[4096] = {0};
  1185. if (0 == count || !ppsz)
  1186. {
  1187. SetWindowText(hwndStatus, L"");
  1188. return;
  1189. }
  1190. buffer[0] = 0x00;
  1191. for (int i = 0; i < count; i++)
  1192. {
  1193. StringCchCatW(buffer, 4096, ppsz[i]);
  1194. StringCchCatW(buffer, 4096, L" ");
  1195. }
  1196. SetWindowTextW(hwndStatus, buffer);
  1197. StringCchCopyW(oldText, 4096, buffer);
  1198. }
  1199. static void SetRating(UINT iItem, INT newRating, HWND hwndList)
  1200. {
  1201. if (0 == newRating) newRating = -1;
  1202. if (iItem < (UINT)itemCache.Size)
  1203. {
  1204. if (g_table && newRating != itemCache.Items[iItem].rating)
  1205. {
  1206. EnterCriticalSection(&g_db_cs);
  1207. nde_scanner_t s = NDE_Table_CreateScanner(g_table);
  1208. if (NDE_Scanner_LocateNDEFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, itemCache.Items[iItem].filename))
  1209. {
  1210. NDE_Scanner_Edit(s);
  1211. db_setFieldInt(s, MAINTABLE_ID_RATING, newRating);
  1212. NDE_Scanner_Post(s);
  1213. itemCache.Items[iItem].rating = newRating;
  1214. if (g_config->ReadInt(L"writeratings", 0))
  1215. {
  1216. wchar_t buf[64] = {0};
  1217. if (newRating > 0)
  1218. {
  1219. wsprintfW(buf, L"%d", newRating);
  1220. }
  1221. else
  1222. buf[0] = 0;
  1223. updateFileInfo(itemCache.Items[iItem].filename, DB_FIELDNAME_rating, buf);
  1224. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_WRITE_EXTENDED_FILE_INFO);
  1225. }
  1226. }
  1227. NDE_Table_DestroyScanner(g_table, s);
  1228. if (g_table_dirty)
  1229. {
  1230. g_table_dirty = 0;
  1231. NDE_Table_Sync(g_table);
  1232. }
  1233. LeaveCriticalSection(&g_db_cs);
  1234. }
  1235. if (newRating == itemCache.Items[iItem].rating && hwndList)
  1236. {
  1237. ratingColumn.hwndList = hwndList;
  1238. ratingColumn.iItem = iItem;
  1239. ratingColumn.iSubItem = 600;
  1240. MLRatingColumn_Animate(plugin.hwndLibraryParent, &ratingColumn);
  1241. ListView_RedrawItems(resultlist.getwnd(), iItem, iItem);
  1242. // TODO: benski> update the top panes w/o refreshing, if possible
  1243. // CUT: PostMessage(GetParent(GetParent(hwndList)), WM_APP + 4, (WPARAM)newRating, (LPARAM)1);
  1244. }
  1245. }
  1246. }
  1247. /////////// Header Messages / Notifications
  1248. static BOOL Header_OnItemChanging(HWND hwndDlg, NMHEADERW *phdr, LRESULT *pResult, UINT uMsg)
  1249. {
  1250. if (phdr->pitem && (HDI_WIDTH & phdr->pitem->mask))
  1251. {
  1252. INT test;
  1253. test = columnList[columnOrder[phdr->iItem]].minWidth;
  1254. if (phdr->pitem->cxy < test) phdr->pitem->cxy = test;
  1255. test = columnList[columnOrder[phdr->iItem]].maxWidth;
  1256. if (test != UNLIMITED_WIDTH && phdr->pitem->cxy > test) phdr->pitem->cxy = test;
  1257. if (MEDIAVIEW_COL_RATING == columnOrder[phdr->iItem])
  1258. {
  1259. RATINGWIDTH rw;
  1260. rw.fStyle = RCS_DEFAULT;
  1261. rw.width = phdr->pitem->cxy;
  1262. if (MLRatingColumn_GetWidth(plugin.hwndLibraryParent, &rw)) phdr->pitem->cxy = rw.width;
  1263. if (0 == phdr->iItem)
  1264. {
  1265. RATINGBACKTEXT rbt;
  1266. rbt.pszText = ratingBackText;
  1267. rbt.cchTextMax = sizeof(ratingBackText)/sizeof(WCHAR);
  1268. rbt.nColumnWidth = phdr->pitem->cxy;
  1269. rbt.fStyle = RCS_DEFAULT;
  1270. MLRatingColumn_FillBackString(plugin.hwndLibraryParent, &rbt);
  1271. }
  1272. else ratingBackText[0] = 0x00;
  1273. }
  1274. else if (MEDIAVIEW_COL_CLOUD == columnOrder[phdr->iItem])
  1275. {
  1276. if (!cloud_hinst || cloud_hinst == (HINSTANCE)1 ||
  1277. !SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE))
  1278. phdr->pitem->cxy = 0;
  1279. else
  1280. {
  1281. INT width = phdr->pitem->cxy;
  1282. if (MLCloudColumn_GetWidth(plugin.hwndLibraryParent, &width))
  1283. {
  1284. phdr->pitem->cxy = width;
  1285. }
  1286. }
  1287. }
  1288. }
  1289. return FALSE;
  1290. }
  1291. static BOOL Header_OnEndDrag(HWND hwndDlg, NMHEADERW *phdr, LRESULT *pResult)
  1292. {
  1293. PostMessageW(hwndDlg, IM_SYNCHEADERORDER, 0, (LPARAM)phdr->hdr.hwndFrom);
  1294. return FALSE;
  1295. }
  1296. static BOOL Header_OnRightClick(HWND hwndDlg, NMHDR *pnmh, LRESULT *pResult)
  1297. {
  1298. HMENU menu = GetSubMenu(g_context_menus, 4);
  1299. POINT p;
  1300. GetCursorPos(&p);
  1301. int r = DoTrackPopup(menu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, p.x, p.y, hwndDlg, NULL);
  1302. switch (r)
  1303. {
  1304. case ID_HEADERWND_CUSTOMIZECOLUMNS:
  1305. customizeColumnsDialog(hwndDlg);
  1306. break;
  1307. }
  1308. return FALSE;
  1309. }
  1310. /////////// ListView Messages / Notifications
  1311. static BOOL ListView_OnItemChanged(HWND hwndDlg, NMLISTVIEW *pnmv)
  1312. {
  1313. if (pnmv->uNewState & LVIS_SELECTED)
  1314. {
  1315. //if (GetFocus()==resultlist.getwnd())
  1316. {
  1317. m_last_selitem = pnmv->iItem;
  1318. KillTimer(hwndDlg, 6600);
  1319. SetTimer(hwndDlg, 6600, 250, NULL);
  1320. }
  1321. }
  1322. else
  1323. {
  1324. if (isMixablePresent)
  1325. {
  1326. SetDlgItemText(hwndDlg, IDC_MIXABLE, L"");
  1327. isMixablePresent = false;
  1328. }
  1329. }
  1330. return FALSE;
  1331. }
  1332. static BOOL ListView_OnDoubleClick(HWND hwndDlg, NMITEMACTIVATE *pnmitem)
  1333. {
  1334. playFiles((!!g_config->ReadInt(L"enqueuedef", 0)) ^(!!(GetAsyncKeyState(VK_SHIFT)&0x8000)), 0);
  1335. return FALSE;
  1336. }
  1337. void EatKeyboard()
  1338. {
  1339. Sleep(100);
  1340. MSG msg;
  1341. while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); //eat return
  1342. }
  1343. static void Dialog_OnContextMenu(HWND hwndDlg, HWND hwndFrom, int x, int y)
  1344. {
  1345. if (hwndFrom != resultlist.getwnd())
  1346. return;
  1347. POINT pt = {x,y};
  1348. if (x == -1 || y == -1) // x and y are -1 if the user invoked a shift-f10 popup menu
  1349. {
  1350. RECT itemRect = {0};
  1351. int selected = resultlist.GetNextSelected();
  1352. if (selected != -1) // if something is selected we'll drop the menu from there
  1353. {
  1354. resultlist.GetItemRect(selected, &itemRect);
  1355. ClientToScreen(resultlist.getwnd(), (POINT *)&itemRect);
  1356. }
  1357. else // otherwise we'll drop it from the top-left corner of the listview, adjusting for the header location
  1358. {
  1359. GetWindowRect(resultlist.getwnd(), &itemRect);
  1360. HWND hHeader = (HWND)SNDMSG(resultlist.getwnd(), LVM_GETHEADER, 0, 0L);
  1361. RECT headerRect;
  1362. if ((WS_VISIBLE & GetWindowLongPtr(hHeader, GWL_STYLE)) && GetWindowRect(hHeader, &headerRect))
  1363. {
  1364. itemRect.top += (headerRect.bottom - headerRect.top);
  1365. }
  1366. }
  1367. x = itemRect.left;
  1368. y = itemRect.top;
  1369. }
  1370. HWND hHeader = (HWND)SNDMSG(resultlist.getwnd(), LVM_GETHEADER, 0, 0L);
  1371. RECT headerRect;
  1372. if (0 == (WS_VISIBLE & GetWindowLongPtr(hHeader, GWL_STYLE)) || FALSE == GetWindowRect(hHeader, &headerRect))
  1373. {
  1374. SetRectEmpty(&headerRect);
  1375. }
  1376. if (FALSE != PtInRect(&headerRect, pt))
  1377. {
  1378. return;
  1379. }
  1380. HMENU globmenu = WASABI_API_LOADMENU(IDR_CONTEXTMENUS);
  1381. HMENU menu = GetSubMenu(globmenu, 0);
  1382. int rate_idx = 9;
  1383. sendto_hmenu = GetSubMenu(menu, 2);
  1384. rate_hmenu = GetSubMenu(menu, rate_idx);
  1385. ConvertRatingMenuStar(rate_hmenu, ID_RATE_5);
  1386. ConvertRatingMenuStar(rate_hmenu, ID_RATE_4);
  1387. ConvertRatingMenuStar(rate_hmenu, ID_RATE_3);
  1388. ConvertRatingMenuStar(rate_hmenu, ID_RATE_2);
  1389. ConvertRatingMenuStar(rate_hmenu, ID_RATE_1);
  1390. s.mode = 0;
  1391. s.hwnd = 0;
  1392. s.build_hMenu = 0;
  1393. IPC_LIBRARY_SENDTOMENU = SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"LibrarySendToMenu", IPC_REGISTER_WINAMP_IPCMESSAGE);
  1394. if (IPC_LIBRARY_SENDTOMENU > 65536 && SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)0, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
  1395. {
  1396. s.mode = 1;
  1397. s.hwnd = hwndDlg;
  1398. s.data_type = ML_TYPE_ITEMRECORDLIST;
  1399. s.ctx[1] = 1;
  1400. s.build_hMenu = sendto_hmenu;
  1401. }
  1402. wchar_t *artist = NULL;
  1403. wchar_t *album = NULL;
  1404. int n = resultlist.GetSelectionMark();
  1405. if (n != -1)
  1406. {
  1407. artist = itemCache.Items[n].artist;
  1408. album = itemCache.Items[n].album;
  1409. wchar_t str[2048] = {0}, str2[128] = {0};
  1410. // (BigG): Check the ini settings for viewing vs playing and create the menu accordingly
  1411. // Keeping this here in case we want to use the ini setting:
  1412. //StringCchPrintfW(str, 2048, WASABI_API_LNGSTRINGW( (g_viewnotplay != 0) ? IDS_VIEW_ALL_FILES_BY : IDS_PLAY_ALL_FILES_BY), artist ? artist : L"");
  1413. int len = lstrlenW(artist);
  1414. if (len > 39)
  1415. {
  1416. StringCchPrintfW(str2, 40, L"%.36s...", artist);//WASABI_API_LNGSTRINGW(IDS_VIEW_ALL_FILES_BY), artist ? artist : L"");
  1417. StringCchPrintfW(str, 2048, WASABI_API_LNGSTRINGW(IDS_VIEW_ALL_FILES_BY), str2);
  1418. }
  1419. else
  1420. {
  1421. StringCchPrintfW(str, 2048, WASABI_API_LNGSTRINGW(IDS_VIEW_ALL_FILES_BY), artist ? artist : L"");
  1422. }
  1423. FixAmps(str, 2048);
  1424. MENUITEMINFOW mii =
  1425. {
  1426. sizeof(MENUITEMINFOW),
  1427. MIIM_TYPE | MIIM_ID,
  1428. MFT_STRING,
  1429. MFS_ENABLED,
  1430. 0x1234,
  1431. NULL,
  1432. NULL,
  1433. NULL,
  1434. 0,
  1435. str,
  1436. 0,
  1437. };
  1438. if (!(!cloud_hinst || cloud_hinst == (HINSTANCE)1 ||
  1439. !SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE)))
  1440. {
  1441. MENUITEMINFOW m = {sizeof(m), MIIM_TYPE | MIIM_ID | MIIM_SUBMENU, MFT_SEPARATOR, 0};
  1442. m.wID = CLOUD_SOURCE_MENUS - 1;
  1443. InsertMenuItemW(menu, 0, FALSE, &m);
  1444. wchar_t a[100] = {0};
  1445. m.fType = MFT_STRING;
  1446. m.dwTypeData = WASABI_API_LNGSTRINGW_BUF(IDS_CLOUD_SOURCES, a, 100);
  1447. m.wID = CLOUD_SOURCE_MENUS;
  1448. m.hSubMenu = cloud_hmenu = CreatePopupMenu();
  1449. InsertMenuItemW(menu, 0, FALSE, &m);
  1450. }
  1451. if (artist && artist[0])
  1452. {
  1453. InsertMenuItemW(menu, ID_EDITITEMINFOS, FALSE, &mii);
  1454. }
  1455. // (BigG): Check the ini settings for viewing vs playing and create the menu accordingly
  1456. // Keeping this here in case we want to use the ini setting:
  1457. len = lstrlenW(album);
  1458. if (len > 39)
  1459. {
  1460. StringCchPrintfW(str2, 40, L"%.36s...", album);
  1461. StringCchPrintfW(str, 2048, WASABI_API_LNGSTRINGW(IDS_VIEW_ALL_FILES_FROM), str2);
  1462. }
  1463. else
  1464. {
  1465. StringCchPrintfW(str, 2048, WASABI_API_LNGSTRINGW(IDS_VIEW_ALL_FILES_FROM), album ? album : L"");
  1466. }
  1467. FixAmps(str, 2048);
  1468. mii.cch = wcslen(str);
  1469. mii.wID = 0x1235;
  1470. if (album && album[0])
  1471. {
  1472. InsertMenuItemW(menu, ID_EDITITEMINFOS, FALSE, &mii);
  1473. }
  1474. {
  1475. mii.wID = 0xdeadbeef;
  1476. mii.fType = MFT_SEPARATOR;
  1477. InsertMenuItemW(menu, ID_EDITITEMINFOS, FALSE, &mii);
  1478. }
  1479. }
  1480. else
  1481. {
  1482. EnableMenuItem(menu, ID_MEDIAWND_PLAYSELECTEDFILES, MF_BYCOMMAND | MF_GRAYED);
  1483. EnableMenuItem(menu, ID_MEDIAWND_ENQUEUESELECTEDFILES, MF_BYCOMMAND | MF_GRAYED);
  1484. EnableMenuItem(menu, IDC_REFRESH_METADATA, MF_BYCOMMAND | MF_GRAYED);
  1485. EnableMenuItem(menu, ID_MEDIAWND_REMOVEFROMLIBRARY, MF_BYCOMMAND | MF_GRAYED);
  1486. EnableMenuItem(menu, ID_EDITITEMINFOS, MF_BYCOMMAND | MF_GRAYED);
  1487. EnableMenuItem(menu, ID_MEDIAWND_EXPLOREFOLDER, MF_BYCOMMAND | MF_GRAYED);
  1488. EnableMenuItem(menu, ID_PE_ID3, MF_BYCOMMAND | MF_GRAYED);
  1489. EnableMenuItem(menu, 2, MF_BYPOSITION | MF_GRAYED); //grays the "Add to playlist..." menu
  1490. EnableMenuItem(menu, rate_idx, MF_BYPOSITION | MF_GRAYED); //grays the "Rate..." menu
  1491. EnableMenuItem(menu, 13, MF_BYPOSITION | MF_GRAYED); //grays the "Remove..." menu
  1492. }
  1493. menufucker_t mf = {sizeof(mf),MENU_MEDIAVIEW,menu,0x3000,0x4000,0};
  1494. mf.extinf.mediaview.list = resultlist.getwnd();
  1495. mf.extinf.mediaview.items = &itemCache;
  1496. pluginMessage message_build = {ML_IPC_MENUFUCKER_BUILD,(intptr_t)&mf,0};
  1497. SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&message_build, ML_IPC_SEND_PLUGIN_MESSAGE);
  1498. Menu_SetRatingValue(rate_hmenu, 0);
  1499. UpdateMenuItems(hwndDlg, menu, IDR_VIEW_ACCELERATORS);
  1500. int r = DoTrackPopup(menu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, x, y, hwndDlg, NULL);
  1501. pluginMessage message_result = {ML_IPC_MENUFUCKER_RESULT,(intptr_t)&mf,r,0};
  1502. SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&message_result, ML_IPC_SEND_PLUGIN_MESSAGE);
  1503. switch (r)
  1504. {
  1505. case ID_MEDIAWND_PLAYSELECTEDFILES:
  1506. case ID_MEDIAWND_ENQUEUESELECTEDFILES:
  1507. playFiles((r == ID_MEDIAWND_ENQUEUESELECTEDFILES), 0);
  1508. break;
  1509. case ID_MEDIAWND_SELECTALL:
  1510. {
  1511. LVITEM item = {0};
  1512. item.state = LVIS_SELECTED;
  1513. item.stateMask = LVIS_SELECTED;
  1514. SendMessageW(hwndFrom, LVM_SETITEMSTATE, (WPARAM)-1, (LPARAM)&item);
  1515. }
  1516. break;
  1517. case ID_MEDIAWND_REMOVEFROMLIBRARY:
  1518. removeSelectedItems(0);
  1519. break;
  1520. case ID_EDITITEMINFOS:
  1521. if (resultlist.GetSelectedCount() > 0)
  1522. editInfo(hwndDlg);
  1523. break;
  1524. case ID_PE_ID3:
  1525. fileInfoDialogs(hwndDlg);
  1526. PostMessageW(hwndDlg, WM_NEXTDLGCTL, (WPARAM)hwndFrom, (LPARAM)TRUE);
  1527. break;
  1528. case IDC_REFRESH_METADATA:
  1529. RefreshMetadata(hwndDlg);
  1530. break;
  1531. case 0x1234: // all files from selected artist
  1532. {
  1533. wchar_t tmp[2048] = {0};
  1534. GayStringW escaped;
  1535. queryStrEscape(artist, escaped);
  1536. // Keeping this here in case we want to use the ini setting later:
  1537. //if ( g_viewnotplay ) // (BigG): Check to see if we should play or view the current tracks
  1538. //{
  1539. StringCchPrintfW(tmp, 2048, L"?artist = \"%s\"", escaped.Get());
  1540. SetWindowTextW(hwndSearchGlobal, tmp);
  1541. //}
  1542. //else // Otherwise do the old behavior and just play it back
  1543. //{
  1544. // StringCchPrintfW(tmp, 2048, L"artist = \"%s\"", escaped.Get());
  1545. // main_playQuery(g_view_metaconf, tmp, 0);
  1546. //}
  1547. SetWindowTextW(hwndSearchGlobal, tmp);
  1548. }
  1549. break;
  1550. case 0x1235: // all files from selected album
  1551. {
  1552. wchar_t tmp[2048] = {0};
  1553. GayStringW escaped;
  1554. queryStrEscape(album, escaped);
  1555. // Keeping this here in case we want to use the ini setting later:
  1556. //if ( g_viewnotplay ) // (BigG): Check to see if we should play or view the current tracks
  1557. //{
  1558. StringCchPrintfW(tmp, 2048, L"?album = \"%s\"", escaped.Get());
  1559. SetWindowTextW(hwndSearchGlobal, tmp);
  1560. //}
  1561. //else // Otherwise do the old behavior and just play it back
  1562. //{
  1563. // StringCchPrintfW(tmp, 2048, L"album = \"%s\"", escaped.Get());
  1564. // main_playQuery(g_view_metaconf, tmp, 0);
  1565. //}
  1566. }
  1567. break;
  1568. case ID_RATE_1:
  1569. case ID_RATE_2:
  1570. case ID_RATE_3:
  1571. case ID_RATE_4:
  1572. case ID_RATE_5:
  1573. case ID_RATE_0:
  1574. {
  1575. int rate = r - ID_RATE_1 + 1;
  1576. if (r == ID_RATE_0) rate = 0;
  1577. int x;
  1578. int has = 0;
  1579. EnterCriticalSection(&g_db_cs);
  1580. nde_scanner_t s = NDE_Table_CreateScanner(g_table);
  1581. for (x = 0; x < itemCache.Size; x ++)
  1582. {
  1583. if (resultlist.GetSelected(x))
  1584. {
  1585. if (NDE_Scanner_LocateNDEFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, itemCache.Items[x].filename))
  1586. {
  1587. has++;
  1588. NDE_Scanner_Edit(s);
  1589. db_setFieldInt(s, MAINTABLE_ID_RATING, rate);
  1590. NDE_Scanner_Post(s);
  1591. itemCache.Items[x].rating = rate;
  1592. if (g_config->ReadInt(L"writeratings", 0))
  1593. {
  1594. wchar_t buf[64] = {0};
  1595. if (rate > 0)
  1596. {
  1597. wsprintfW(buf, L"%d", rate);
  1598. }
  1599. else
  1600. buf[0] = 0;
  1601. updateFileInfo(itemCache.Items[x].filename, DB_FIELDNAME_rating, buf);
  1602. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_WRITE_EXTENDED_FILE_INFO);
  1603. }
  1604. }
  1605. }
  1606. }
  1607. NDE_Table_DestroyScanner(g_table, s);
  1608. if (g_table_dirty)
  1609. {
  1610. g_table_dirty = 0;
  1611. NDE_Table_Sync(g_table);
  1612. }
  1613. LeaveCriticalSection(&g_db_cs);
  1614. if (has)
  1615. {
  1616. ListView_RedrawItems(resultlist.getwnd(), 0, itemCache.Size - 1);
  1617. PostMessage(GetParent(hwndDlg), WM_APP + 4, (WPARAM)rate, (LPARAM)0);
  1618. }
  1619. }
  1620. break;
  1621. case ID_MEDIAWND_EXPLOREFOLDER:
  1622. exploreItemFolder(hwndDlg);
  1623. break;
  1624. case ID_MEDIAWND_REMOVE_REMOVEALLDEADFILES:
  1625. removeDeadFiles(hwndDlg);
  1626. break;
  1627. case ID_MEDIAWND_REMOVE_PHYSICALLYREMOVESELECTEDITEMS:
  1628. RecycleSelectedItems();
  1629. break;
  1630. default:
  1631. {
  1632. if (cloud_hmenu && (r >= CLOUD_SOURCE_MENUS && r < CLOUD_SOURCE_MENUS_UPPER)) // deals with cloud specific menus
  1633. {
  1634. // 0 = no change
  1635. // 1 = add cloud
  1636. // 2 = add local
  1637. // 4 = removed
  1638. int mode = 0;
  1639. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_PROCESS_CLOUD_STATUS, (intptr_t)r, (intptr_t)&mode);
  1640. int n = resultlist.GetSelectionMark();
  1641. if (n != -1)
  1642. {
  1643. switch (mode)
  1644. {
  1645. case 1:
  1646. setCloudValue(&itemCache.Items[n], L"5");
  1647. break;
  1648. case 2:
  1649. setCloudValue(&itemCache.Items[n], L"0");
  1650. break;
  1651. case 4:
  1652. setCloudValue(&itemCache.Items[n], L"4");
  1653. break;
  1654. }
  1655. InvalidateRect(resultlist.getwnd(), NULL, TRUE);
  1656. }
  1657. break;
  1658. }
  1659. if (s.mode == 2)
  1660. {
  1661. s.menu_id = r;
  1662. if (SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
  1663. {
  1664. // build my data.
  1665. s.mode = 3;
  1666. s.data_type = ML_TYPE_ITEMRECORDLISTW;
  1667. itemRecordListW myObj = {0, };
  1668. copyFilesToItemCacheW(&myObj); // does not dupe strings
  1669. s.data = (void*) & myObj;
  1670. LRESULT result = SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM) & s, IPC_LIBRARY_SENDTOMENU);
  1671. if (result != 1)
  1672. {
  1673. s.mode = 3;
  1674. s.data_type = ML_TYPE_ITEMRECORDLIST;
  1675. itemRecordList objA = {0, };
  1676. convertRecordList(&objA, &myObj);
  1677. s.data = (void*) & objA;
  1678. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU);
  1679. freeRecordList(&objA);
  1680. }
  1681. _aligned_free(myObj.Items);
  1682. }
  1683. }
  1684. break;
  1685. }
  1686. }
  1687. if (s.mode)
  1688. {
  1689. s.mode = 4;
  1690. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU); // cleanup
  1691. }
  1692. sendto_hmenu = 0;
  1693. DestroyMenu(cloud_hmenu);
  1694. cloud_hmenu = 0;
  1695. DestroyMenu(globmenu);
  1696. UpdateWindow(hwndFrom);
  1697. EatKeyboard();
  1698. }
  1699. static BOOL ListView_OnFindItem(HWND hwndDlg, NMLVFINDITEMW *pfi, LRESULT *pResult, UINT uMsg)
  1700. {
  1701. if (bgThread_Handle) return FALSE;
  1702. int i = pfi->iStart;
  1703. if (i >= itemCache.Size) i = 0;
  1704. int cnt = itemCache.Size - i;
  1705. if (pfi->lvfi.flags & LVFI_WRAP) cnt += i;
  1706. int by = g_view_metaconf->ReadInt(L"mv_sort_by", MEDIAVIEW_COL_ARTIST);
  1707. while (cnt-- > 0)
  1708. {
  1709. itemRecordW *thisitem = itemCache.Items + i;
  1710. wchar_t tmp[128] = {0};
  1711. wchar_t *name = 0;
  1712. switch (by)
  1713. {
  1714. case MEDIAVIEW_COL_FILENAME:
  1715. name = thisitem->filename + wcslen(thisitem->filename);
  1716. while (name >= thisitem->filename && *name != L'/' && *name != L'\\') name--;
  1717. break;
  1718. case MEDIAVIEW_COL_FULLPATH:
  1719. name = thisitem->filename;
  1720. break;
  1721. case MEDIAVIEW_COL_EXTENSION:
  1722. name = PathFindExtensionW(thisitem->filename);
  1723. if (name && *name)
  1724. name++;
  1725. break;
  1726. case MEDIAVIEW_COL_TITLE: name = thisitem->title; break;
  1727. case MEDIAVIEW_COL_COMMENT: name = thisitem->comment; break;
  1728. case MEDIAVIEW_COL_ARTIST: name = thisitem->artist; break;
  1729. case MEDIAVIEW_COL_ALBUM: name = thisitem->album; break;
  1730. case MEDIAVIEW_COL_GENRE: name = thisitem->genre; break;
  1731. case MEDIAVIEW_COL_YEAR:
  1732. tmp[0] = 0;
  1733. if (thisitem->year >= 0) StringCchPrintfW(tmp, 128, L"%04d", thisitem->year);
  1734. name = tmp;
  1735. break;
  1736. case MEDIAVIEW_COL_TRACK:
  1737. tmp[0] = 0;
  1738. if (thisitem->track > 0)
  1739. {
  1740. if (thisitem->tracks > 0) StringCchPrintfW(tmp, 128, L"%d/%d", thisitem->track, thisitem->tracks);
  1741. else StringCchPrintfW(tmp, 128, L"%d", thisitem->track);
  1742. }
  1743. name = tmp;
  1744. break;
  1745. case MEDIAVIEW_COL_LENGTH:
  1746. tmp[0] = 0;
  1747. if (thisitem->length >= 0) StringCchPrintfW(tmp, 128, L"%d:%02d", thisitem->length / 60, thisitem->length % 60);
  1748. name = tmp;
  1749. break;
  1750. case MEDIAVIEW_COL_RATING:
  1751. tmp[0] = 0;
  1752. if (thisitem->rating > 0) StringCchPrintfW(tmp, 128, L"%d", thisitem->rating);
  1753. name = tmp;
  1754. break;
  1755. case MEDIAVIEW_COL_CLOUD:
  1756. {
  1757. tmp[0] = 0;
  1758. wchar_t *x = getRecordExtendedItem_fast(thisitem, extended_fields.cloud);
  1759. if (x && *x) StringCchPrintfW(tmp, 128, L"%d", x);
  1760. name = tmp;
  1761. break;
  1762. }
  1763. case MEDIAVIEW_COL_PLAYCOUNT:
  1764. tmp[0] = 0;
  1765. if (thisitem->playcount > 0) StringCchPrintfW(tmp, 128, L"%d", thisitem->playcount);
  1766. name = tmp;
  1767. break;
  1768. case MEDIAVIEW_COL_BITRATE:
  1769. if (thisitem->bitrate > 0)
  1770. StringCchPrintfW(name = tmp, 128, L"%d%s", thisitem->bitrate, WASABI_API_LNGSTRINGW(IDS_KBPS));
  1771. break;
  1772. case MEDIAVIEW_COL_BPM:
  1773. if (thisitem->bpm > 0)
  1774. StringCchPrintfW(name = tmp, 128, L"%d", thisitem->bpm);
  1775. break;
  1776. case MEDIAVIEW_COL_TYPE:
  1777. name = WASABI_API_LNGSTRINGW(thisitem->type ? IDS_VIDEO : IDS_AUDIO);
  1778. break;
  1779. case MEDIAVIEW_COL_DISC:
  1780. tmp[0] = 0;
  1781. if (thisitem->disc > 0)
  1782. {
  1783. if (thisitem->discs > 0)
  1784. StringCchPrintfW(tmp, 128, L"%d/%d", thisitem->disc, thisitem->discs);
  1785. else
  1786. StringCchPrintfW(tmp, 128, L"%d", thisitem->disc);
  1787. }
  1788. name = tmp;
  1789. break;
  1790. case MEDIAVIEW_COL_ALBUMARTIST:
  1791. name = thisitem->albumartist;
  1792. break;
  1793. case MEDIAVIEW_COL_PUBLISHER:
  1794. name = thisitem->publisher;
  1795. break;
  1796. case MEDIAVIEW_COL_COMPOSER:
  1797. name = thisitem->composer;
  1798. break;
  1799. case MEDIAVIEW_COL_ALBUMGAIN:
  1800. name = thisitem->replaygain_album_gain;
  1801. break;
  1802. case MEDIAVIEW_COL_TRACKGAIN:
  1803. name = thisitem->replaygain_track_gain;
  1804. break;
  1805. case MEDIAVIEW_COL_FILESIZE:
  1806. tmp[0] = 0;
  1807. if (thisitem->filesize > 0) StringCchPrintfW(tmp, 128, L"%d", thisitem->filesize);
  1808. name = tmp;
  1809. break;
  1810. case MEDIAVIEW_COL_FILETIME:
  1811. tmp[0] = 0;
  1812. if (thisitem->filetime > 0) StringCchPrintfW(tmp, 128, L"%d", thisitem->filetime);
  1813. name = tmp;
  1814. break;
  1815. case MEDIAVIEW_COL_LASTUPD:
  1816. tmp[0] = 0;
  1817. if (thisitem->lastupd > 0) StringCchPrintfW(tmp, 128, L"%d", thisitem->lastupd);
  1818. name = tmp;
  1819. break;
  1820. case MEDIAVIEW_COL_DATEADDED:
  1821. {
  1822. tmp[0] = 0;
  1823. wchar_t *x = getRecordExtendedItem_fast(thisitem,extended_fields.dateadded);
  1824. if (x && *x) StringCchPrintfW(tmp, 128, L"%d", x);
  1825. name = tmp;
  1826. break;
  1827. }
  1828. case MEDIAVIEW_COL_LASTPLAY:
  1829. tmp[0] = 0;
  1830. if (thisitem->lastplay > 0) StringCchPrintfW(tmp, 128, L"%d", thisitem->lastplay);
  1831. name = tmp;
  1832. break;
  1833. case MEDIAVIEW_COL_ISPODCAST:
  1834. {
  1835. wchar_t * t = getRecordExtendedItem_fast(thisitem, extended_fields.ispodcast);
  1836. name = WASABI_API_LNGSTRINGW(t && wcscmp(t,L"1") ? IDS_PODCAST : IDS_NON_PODCAST);
  1837. }
  1838. break;
  1839. case MEDIAVIEW_COL_PODCASTCHANNEL:
  1840. name = getRecordExtendedItem_fast(thisitem,extended_fields.podcastchannel);
  1841. break;
  1842. case MEDIAVIEW_COL_PODCASTPUBDATE:
  1843. {
  1844. tmp[0] = 0;
  1845. wchar_t *x = getRecordExtendedItem_fast(thisitem,extended_fields.podcastpubdate);
  1846. if (x && *x) StringCchPrintfW(tmp, 128, L"%d", x);
  1847. name = tmp;
  1848. }
  1849. break;
  1850. case MEDIAVIEW_COL_CATEGORY:
  1851. name = thisitem->category;
  1852. break;
  1853. case MEDIAVIEW_COL_DIRECTOR:
  1854. name = getRecordExtendedItem_fast(thisitem,extended_fields.director);
  1855. break;
  1856. case MEDIAVIEW_COL_PRODUCER:
  1857. name = getRecordExtendedItem_fast(thisitem,extended_fields.producer);
  1858. break;
  1859. case MEDIAVIEW_COL_DIMENSION:
  1860. {
  1861. tmp[0] = 0;
  1862. wchar_t *w = getRecordExtendedItem_fast(thisitem,extended_fields.width);
  1863. wchar_t *h = getRecordExtendedItem_fast(thisitem,extended_fields.height);
  1864. if (w && *w && h && *h) StringCchPrintfW(tmp, 128, L"%dx%d", _wtoi(w), _wtoi(h));
  1865. name = tmp;
  1866. break;
  1867. }
  1868. }
  1869. if (!name) name = L"";
  1870. else SKIP_THE_AND_WHITESPACEW(name)
  1871. if (pfi->lvfi.flags & (4 | LVFI_PARTIAL))
  1872. {
  1873. if (!StrCmpNIW(name, pfi->lvfi.psz, wcslen(pfi->lvfi.psz)))
  1874. {
  1875. *pResult = i;
  1876. return TRUE;
  1877. }
  1878. }
  1879. else if (pfi->lvfi.flags & LVFI_STRING)
  1880. {
  1881. if (!lstrcmpiW(name, pfi->lvfi.psz))
  1882. {
  1883. *pResult = i;
  1884. return TRUE;
  1885. }
  1886. }
  1887. else
  1888. {
  1889. *pResult = i;
  1890. return TRUE;
  1891. }
  1892. if (++i == itemCache.Size) i = 0;
  1893. }
  1894. *pResult = i;
  1895. return TRUE;
  1896. }
  1897. static BOOL ListView_OnGetDispInfo(HWND hwndDlg, NMLVDISPINFOW* pdi, UINT uMsg)
  1898. {
  1899. LVITEMW *pItem;
  1900. itemRecordW *pRec;
  1901. pItem = &pdi->item;
  1902. if (bgThread_Handle)
  1903. {
  1904. if (0 == pItem->iItem && 0 == pItem->iSubItem && (LVIF_TEXT & pItem->mask))
  1905. {
  1906. static char bufpos = 0;
  1907. static int buflen = 17;
  1908. static wchar_t buffer[64];//L"Scanning _\0/-\\|";
  1909. if (!buffer[0])
  1910. {
  1911. WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_PLAIN,buffer,54);
  1912. StringCchCatW(buffer,64,L" _");
  1913. StringCchCatW(buffer+(buflen=lstrlenW(buffer)+1),64,L"/-\\|");
  1914. buflen+=4;
  1915. }
  1916. int pos = buflen - 5;;
  1917. buffer[pos - 1] = buffer[pos + (bufpos++&3) + 1];
  1918. pItem->pszText = buffer;
  1919. }
  1920. return FALSE;
  1921. }
  1922. if (pItem->iItem < 0 || pItem->iItem >= itemCache.Size) return FALSE;
  1923. pRec = itemCache.Items + pItem->iItem;
  1924. if (LVIF_TEXT & pItem->mask)
  1925. {
  1926. switch (columnOrder[pItem->iSubItem])
  1927. {
  1928. case MEDIAVIEW_COL_FILENAME: // show filename (Tho we'll show without the path cause paths are gay)
  1929. pItem->pszText = PathFindFileNameW(pRec->filename);
  1930. break;
  1931. case MEDIAVIEW_COL_FULLPATH: // show filename (Tho we'll show without the path cause paths are gay)
  1932. pItem->pszText = pRec->filename;
  1933. break;
  1934. case MEDIAVIEW_COL_EXTENSION:
  1935. pItem->pszText = PathFindExtensionW(pRec->filename);
  1936. if (pItem->pszText && *pItem->pszText) pItem->pszText++;
  1937. break;
  1938. case MEDIAVIEW_COL_TITLE:
  1939. pItem->pszText = pRec->title;
  1940. break;
  1941. case MEDIAVIEW_COL_COMMENT:
  1942. pItem->pszText = pRec->comment;
  1943. break;
  1944. case MEDIAVIEW_COL_ALBUM:
  1945. pItem->pszText = pRec->album;
  1946. break;
  1947. case MEDIAVIEW_COL_ARTIST:
  1948. pItem->pszText = pRec->artist;
  1949. break;
  1950. case MEDIAVIEW_COL_TRACK:
  1951. if (pRec->track > 0)
  1952. {
  1953. if (pRec->tracks > 0) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d/%d", pRec->track, pRec->tracks);
  1954. else StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d", pRec->track);
  1955. }
  1956. break;
  1957. case MEDIAVIEW_COL_GENRE:
  1958. pItem->pszText = pRec->genre;
  1959. break;
  1960. case MEDIAVIEW_COL_YEAR:
  1961. if (pRec->year >= 0) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%04d", pRec->year);
  1962. break;
  1963. case MEDIAVIEW_COL_LENGTH:
  1964. if (pRec->length >= 0) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d:%02d", pRec->length / 60, pRec->length % 60);
  1965. break;
  1966. case MEDIAVIEW_COL_RATING:
  1967. if (0 == pItem->iSubItem && ratingBackText[0]) pItem->pszText = ratingBackText;
  1968. else if (pRec->rating > 0 && pRec->rating <= 5) _itow(pRec->rating, pItem->pszText, 10);
  1969. break;
  1970. case MEDIAVIEW_COL_CLOUD:
  1971. {
  1972. wchar_t *t = getRecordExtendedItem_fast(pRec, extended_fields.cloud);
  1973. if (t && *t) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d", _wtoi(t));
  1974. break;
  1975. }
  1976. case MEDIAVIEW_COL_PLAYCOUNT:
  1977. if (pRec->playcount >= 0) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d", pRec->playcount);
  1978. else pItem->pszText = L"0";
  1979. break;
  1980. case MEDIAVIEW_COL_DISC:
  1981. if (pRec->disc > 0)
  1982. {
  1983. if (pRec->discs > 0) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d/%d", pRec->disc, pRec->discs);
  1984. else StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d", pRec->disc);
  1985. }
  1986. break;
  1987. case MEDIAVIEW_COL_ALBUMARTIST:
  1988. pItem->pszText = pRec->albumartist;
  1989. break;
  1990. case MEDIAVIEW_COL_PUBLISHER:
  1991. pItem->pszText = pRec->publisher;
  1992. break;
  1993. case MEDIAVIEW_COL_COMPOSER:
  1994. pItem->pszText = pRec->composer;
  1995. break;
  1996. case MEDIAVIEW_COL_ALBUMGAIN:
  1997. pItem->pszText = pRec->replaygain_album_gain;
  1998. break;
  1999. case MEDIAVIEW_COL_TRACKGAIN:
  2000. pItem->pszText = pRec->replaygain_track_gain;
  2001. break;
  2002. case MEDIAVIEW_COL_TYPE:
  2003. pItem->pszText = WASABI_API_LNGSTRINGW((pRec->type) ? IDS_VIDEO : IDS_AUDIO);
  2004. break;
  2005. case MEDIAVIEW_COL_BITRATE:
  2006. if (pRec->bitrate > 0) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d%s", pRec->bitrate, WASABI_API_LNGSTRINGW(IDS_KBPS));
  2007. break;
  2008. case MEDIAVIEW_COL_BPM:
  2009. if (pRec->bpm > 0) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d", pRec->bpm);
  2010. break;
  2011. case MEDIAVIEW_COL_FILESIZE:
  2012. if (pRec->filesize != -1) WASABI_API_LNG->FormattedSizeString(pItem->pszText, pItem->cchTextMax, pRec->filesize);
  2013. break;
  2014. case MEDIAVIEW_COL_FILETIME:
  2015. MakeDateString(pRec->filetime, pItem->pszText, pItem->cchTextMax);
  2016. break;
  2017. case MEDIAVIEW_COL_LASTUPD:
  2018. MakeDateString(pRec->lastupd, pItem->pszText, pItem->cchTextMax);
  2019. break;
  2020. case MEDIAVIEW_COL_DATEADDED:
  2021. {
  2022. wchar_t * t = getRecordExtendedItem_fast(pRec,extended_fields.dateadded);
  2023. if (t && *t) MakeDateString(_wtoi64(t), pItem->pszText, pItem->cchTextMax);
  2024. break;
  2025. }
  2026. case MEDIAVIEW_COL_LASTPLAY:
  2027. if (pRec->lastplay > 0) MakeDateString(pRec->lastplay, pItem->pszText, pItem->cchTextMax);
  2028. break;
  2029. case MEDIAVIEW_COL_ISPODCAST:
  2030. {
  2031. wchar_t *t = getRecordExtendedItem_fast(pRec, extended_fields.ispodcast);
  2032. pItem->pszText = WASABI_API_LNGSTRINGW((t && t[0] == L'1' && !t[1]) ? IDS_PODCAST : IDS_NON_PODCAST);
  2033. break;
  2034. }
  2035. case MEDIAVIEW_COL_PODCASTCHANNEL:
  2036. pItem->pszText = getRecordExtendedItem_fast(pRec, extended_fields.podcastchannel);
  2037. break;
  2038. case MEDIAVIEW_COL_PODCASTPUBDATE:
  2039. {
  2040. wchar_t * t = getRecordExtendedItem_fast(pRec,extended_fields.podcastpubdate);
  2041. if (t && *t) MakeDateString(_wtoi64(t), pItem->pszText, pItem->cchTextMax);
  2042. break;
  2043. }
  2044. case MEDIAVIEW_COL_CATEGORY:
  2045. pItem->pszText = pRec->category;
  2046. break;
  2047. case MEDIAVIEW_COL_DIRECTOR:
  2048. pItem->pszText = getRecordExtendedItem_fast(pRec, extended_fields.director);
  2049. break;
  2050. case MEDIAVIEW_COL_PRODUCER:
  2051. pItem->pszText = getRecordExtendedItem_fast(pRec, extended_fields.producer);
  2052. break;
  2053. case MEDIAVIEW_COL_DIMENSION:
  2054. {
  2055. wchar_t *w = getRecordExtendedItem_fast(pRec,extended_fields.width);
  2056. wchar_t *h = getRecordExtendedItem_fast(pRec,extended_fields.height);
  2057. if (w && *w && h && *h) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%dx%d", _wtoi(w), _wtoi(h));
  2058. break;
  2059. }
  2060. }
  2061. if (!pItem->pszText) pItem->pszText = L"";
  2062. }
  2063. return FALSE;
  2064. }
  2065. static BOOL ListView_OnColumnClick(HWND hwndDlg, NMLISTVIEW *pnmv)
  2066. {
  2067. int l_sc = g_view_metaconf->ReadInt(L"mv_sort_by", MEDIAVIEW_COL_ARTIST);
  2068. int l_sd = g_view_metaconf->ReadInt(L"mv_sort_dir", 0);
  2069. if (columnOrder[pnmv->iSubItem] == l_sc) l_sd = !l_sd;
  2070. else
  2071. {
  2072. /* JF> I like this better when the direction doesnt get reset every
  2073. time you choose a new column. To revert to old behavior, uncomment
  2074. this line:
  2075. l_sd=0;
  2076. */
  2077. l_sc = columnOrder[pnmv->iSubItem];
  2078. }
  2079. g_view_metaconf->WriteInt(L"mv_sort_by", l_sc);
  2080. g_view_metaconf->WriteInt(L"mv_sort_dir", l_sd);
  2081. MLSkinnedListView_DisplaySort(pnmv->hdr.hwndFrom, pnmv->iSubItem, !l_sd);
  2082. sortResults(g_view_metaconf, &itemCache);
  2083. resultlist.SetVirtualCount(0);
  2084. resultlist.SetVirtualCount(itemCache.Size); // TODO: we could set a limit here
  2085. ListView_RedrawItems(pnmv->hdr.hwndFrom, 0, itemCache.Size - 1);
  2086. return FALSE;
  2087. }
  2088. static BOOL ListView_OnBeginDrag(HWND hwndDlg, NMLISTVIEW *pnmv)
  2089. {
  2090. switch (columnOrder[pnmv->iSubItem])
  2091. {
  2092. case MEDIAVIEW_COL_RATING:
  2093. ratingColumn.hwndList = pnmv->hdr.hwndFrom;
  2094. ratingColumn.fStyle = RCS_DEFAULT;
  2095. ratingColumn.iItem = pnmv->iItem;
  2096. ratingColumn.iSubItem = pnmv->iSubItem;
  2097. ratingColumn.value = ((UINT)pnmv->iItem < (UINT)itemCache.Size) ? itemCache.Items[pnmv->iItem].rating : 0;
  2098. MLRatingColumn_BeginDrag(plugin.hwndLibraryParent, &ratingColumn);
  2099. break;
  2100. }
  2101. SetCapture(hwndDlg);
  2102. return FALSE;
  2103. }
  2104. static BOOL ListView_OnReturn(HWND hwndDlg, NMHDR *pnmh)
  2105. {
  2106. SendMessage(hwndDlg, WM_COMMAND, ((!!(GetAsyncKeyState(VK_SHIFT)&0x8000)) ^(!!g_config->ReadInt(L"enqueuedef", 0)))
  2107. ? IDC_BUTTON_ENQUEUE : IDC_BUTTON_PLAY, 0);
  2108. return FALSE;
  2109. }
  2110. static BOOL ListView_OnCustomDraw(HWND hwndDlg, NMLVCUSTOMDRAW *plvcd, LRESULT *pResult)
  2111. {
  2112. static BOOL bDrawFocus;
  2113. static RECT rcView;
  2114. static RATINGCOLUMNPAINT ratingColumnPaint;
  2115. static CLOUDCOLUMNPAINT cloudColumnPaint;
  2116. *pResult = CDRF_DODEFAULT;
  2117. switch (plvcd->nmcd.dwDrawStage)
  2118. {
  2119. case CDDS_PREPAINT:
  2120. *pResult |= CDRF_NOTIFYITEMDRAW;
  2121. CopyRect(&rcView, &plvcd->nmcd.rc);
  2122. ratingColumnPaint.fStyle = RCS_DEFAULT;
  2123. ratingColumnPaint.hwndList = plvcd->nmcd.hdr.hwndFrom;
  2124. ratingColumnPaint.hdc = plvcd->nmcd.hdc;
  2125. ratingColumnPaint.prcView = &rcView;
  2126. cloudColumnPaint.hwndList = plvcd->nmcd.hdr.hwndFrom;
  2127. cloudColumnPaint.hdc = plvcd->nmcd.hdc;
  2128. cloudColumnPaint.prcView = &rcView;
  2129. return TRUE;
  2130. case CDDS_ITEMPREPAINT:
  2131. *pResult |= CDRF_NOTIFYSUBITEMDRAW;
  2132. bDrawFocus = (CDIS_FOCUS & plvcd->nmcd.uItemState);
  2133. if (bDrawFocus)
  2134. {
  2135. plvcd->nmcd.uItemState &= ~CDIS_FOCUS;
  2136. *pResult |= CDRF_NOTIFYPOSTPAINT;
  2137. }
  2138. return TRUE;
  2139. case CDDS_ITEMPOSTPAINT:
  2140. if (bDrawFocus)
  2141. {
  2142. RECT rc;
  2143. rc.left = LVIR_BOUNDS;
  2144. SendMessageW(plvcd->nmcd.hdr.hwndFrom, LVM_GETITEMRECT, plvcd->nmcd.dwItemSpec, (LPARAM)&rc);
  2145. rc.left += 3;
  2146. DrawFocusRect(plvcd->nmcd.hdc, &rc);
  2147. plvcd->nmcd.uItemState |= CDIS_FOCUS;
  2148. bDrawFocus = FALSE;
  2149. }
  2150. *pResult = CDRF_SKIPDEFAULT;
  2151. return TRUE;
  2152. case(CDDS_SUBITEM | CDDS_ITEMPREPAINT):
  2153. switch (columnOrder[plvcd->iSubItem])
  2154. {
  2155. case MEDIAVIEW_COL_RATING:
  2156. if (bgThread_Handle || (0 == plvcd->iSubItem && 0 == plvcd->nmcd.rc.right)) break;
  2157. ratingColumnPaint.iItem = plvcd->nmcd.dwItemSpec;
  2158. ratingColumnPaint.iSubItem = plvcd->iSubItem;
  2159. ratingColumnPaint.value = (plvcd->nmcd.dwItemSpec >= 0 && plvcd->nmcd.dwItemSpec < (UINT)itemCache.Size) ? itemCache.Items[plvcd->nmcd.dwItemSpec].rating : 0;
  2160. ratingColumnPaint.prcItem = &plvcd->nmcd.rc;
  2161. ratingColumnPaint.rgbBk = plvcd->clrTextBk;
  2162. ratingColumnPaint.rgbFg = plvcd->clrText;
  2163. if (MLRatingColumn_Paint(plugin.hwndLibraryParent, &ratingColumnPaint))
  2164. {
  2165. *pResult = CDRF_SKIPDEFAULT;
  2166. return TRUE;
  2167. }
  2168. break;
  2169. case MEDIAVIEW_COL_CLOUD:
  2170. if (bgThread_Handle || (0 == plvcd->iSubItem && 0 == plvcd->nmcd.rc.right)) break;
  2171. int icon = 4;
  2172. wchar_t *t = getRecordExtendedItem_fast(&itemCache.Items[plvcd->nmcd.dwItemSpec], extended_fields.cloud);
  2173. if (t && *t) icon = _wtoi(t);
  2174. cloudColumnPaint.iItem = plvcd->nmcd.dwItemSpec;
  2175. cloudColumnPaint.iSubItem = plvcd->iSubItem;
  2176. cloudColumnPaint.value = icon;
  2177. cloudColumnPaint.prcItem = &plvcd->nmcd.rc;
  2178. cloudColumnPaint.rgbBk = plvcd->clrTextBk;
  2179. cloudColumnPaint.rgbFg = plvcd->clrText;
  2180. if (MLCloudColumn_Paint(plugin.hwndLibraryParent, &cloudColumnPaint))
  2181. {
  2182. *pResult = CDRF_SKIPDEFAULT;
  2183. return TRUE;
  2184. }
  2185. break;
  2186. }
  2187. break;
  2188. }
  2189. return FALSE;
  2190. }
  2191. static BOOL ListView_OnClick(HWND hwnDlg, NMITEMACTIVATE *pnmitem)
  2192. {
  2193. if (bgThread_Handle) return FALSE;
  2194. if (pnmitem->iItem != -1)
  2195. {
  2196. switch (columnOrder[pnmitem->iSubItem])
  2197. {
  2198. case MEDIAVIEW_COL_RATING:
  2199. ratingColumn.hwndList = pnmitem->hdr.hwndFrom;
  2200. ratingColumn.ptAction = pnmitem->ptAction;
  2201. ratingColumn.bRedrawNow = TRUE;
  2202. ratingColumn.fStyle = RCS_DEFAULT;
  2203. if (!MLRatingColumn_Click(plugin.hwndLibraryParent, &ratingColumn)) return FALSE;
  2204. SetRating(ratingColumn.iItem, ratingColumn.value, ratingColumn.hwndList);
  2205. break;
  2206. case MEDIAVIEW_COL_CLOUD:
  2207. {
  2208. RECT itemRect = {0};
  2209. if (pnmitem->iSubItem)
  2210. ListView_GetSubItemRect(pnmitem->hdr.hwndFrom, pnmitem->iItem, pnmitem->iSubItem, LVIR_BOUNDS, &itemRect);
  2211. else
  2212. {
  2213. ListView_GetItemRect(pnmitem->hdr.hwndFrom, pnmitem->iItem, &itemRect, LVIR_BOUNDS);
  2214. itemRect.right = itemRect.left + ListView_GetColumnWidth(pnmitem->hdr.hwndFrom, pnmitem->iSubItem);
  2215. }
  2216. MapWindowPoints(pnmitem->hdr.hwndFrom, HWND_DESKTOP, (POINT*)&itemRect, 2);
  2217. HMENU cloud_menu = CreatePopupMenu();
  2218. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_GET_CLOUD_STATUS, (intptr_t)itemCache.Items[pnmitem->iItem].filename, (intptr_t)&cloud_menu);
  2219. if (cloud_menu)
  2220. {
  2221. int r = DoTrackPopup(cloud_menu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, itemRect.right, itemRect.top, pnmitem->hdr.hwndFrom, NULL);
  2222. if (r >= CLOUD_SOURCE_MENUS && r < CLOUD_SOURCE_MENUS_UPPER)
  2223. {
  2224. // 0 = no change
  2225. // 1 = adding to cloud
  2226. // 2 = added locally
  2227. // 4 = removed
  2228. int mode = 0; // deals with cloud specific menus
  2229. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_PROCESS_CLOUD_STATUS, (intptr_t)r, (intptr_t)&mode);
  2230. switch (mode)
  2231. {
  2232. case 1:
  2233. setCloudValue(&itemCache.Items[pnmitem->iItem], L"5");
  2234. break;
  2235. case 2:
  2236. setCloudValue(&itemCache.Items[pnmitem->iItem], L"4");
  2237. break;
  2238. case 4:
  2239. setCloudValue(&itemCache.Items[pnmitem->iItem], L"4");
  2240. break;
  2241. }
  2242. InvalidateRect(resultlist.getwnd(), NULL, TRUE);
  2243. }
  2244. DestroyMenu(cloud_menu);
  2245. }
  2246. }
  2247. break;
  2248. }
  2249. }
  2250. return FALSE;
  2251. }
  2252. static BOOL ListView_OnHotTrack(HWND hwndDlg, NMLISTVIEW *pnmlv, LRESULT *pResult)
  2253. {
  2254. UINT iItem;
  2255. if (bgThread_Handle)
  2256. {
  2257. pnmlv->iItem = -1;
  2258. *pResult = TRUE;
  2259. return TRUE;
  2260. }
  2261. if (-1 == pnmlv->iItem && 0 == pnmlv->iSubItem)
  2262. {
  2263. LVHITTESTINFO lvhit;
  2264. lvhit.pt = pnmlv->ptAction;
  2265. SendMessageW(pnmlv->hdr.hwndFrom, LVM_HITTEST, 0, (LPARAM)&lvhit);
  2266. iItem = lvhit.iItem;
  2267. }
  2268. else iItem = pnmlv->iItem;
  2269. switch (columnOrder[pnmlv->iSubItem])
  2270. {
  2271. case MEDIAVIEW_COL_RATING:
  2272. ratingColumn.hwndList = pnmlv->hdr.hwndFrom;
  2273. ratingColumn.fStyle = RCS_DEFAULT;
  2274. ratingColumn.iItem = iItem;
  2275. ratingColumn.iSubItem = pnmlv->iSubItem;
  2276. ratingColumn.value = (iItem < (UINT)itemCache.Size) ? itemCache.Items[iItem].rating : 0;
  2277. ratingColumn.ptAction = pnmlv->ptAction;
  2278. ratingColumn.bRedrawNow = TRUE;
  2279. MLRatingColumn_Track(plugin.hwndLibraryParent, &ratingColumn);
  2280. break;
  2281. }
  2282. // LVS_EX_ONECLICKACTIVATE enabled - make listview select nothing
  2283. pnmlv->iItem = -1;
  2284. *pResult = TRUE;
  2285. return TRUE;
  2286. }
  2287. /////////// Dialog Messages / Notifications
  2288. static void Dialog_OnDisplayChange(HWND hwndDlg)
  2289. {
  2290. INT i;
  2291. HWND hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
  2292. for (i = 0; -1 != columnOrder[i] && MEDIAVIEW_COL_RATING != columnOrder[i] && MEDIAVIEW_COL_CLOUD != columnOrder[i]; i++);
  2293. if (-1 != columnOrder[i])
  2294. {
  2295. if (hwndList)
  2296. {
  2297. INT w = (INT)SendMessageW(hwndList, LVM_GETCOLUMNWIDTH, i, 0L);
  2298. SendMessageW(hwndList, LVM_SETCOLUMNWIDTH, i, (LPARAM)w);
  2299. }
  2300. }
  2301. LayoutWindows(hwndDlg, TRUE);
  2302. }
  2303. static wchar_t tt_buf[256] = {L""};
  2304. static int last_item = -1, last_icon = -1;
  2305. LRESULT pmp_listview(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  2306. if (uMsg == WM_NOTIFY)
  2307. {
  2308. LPNMHDR l=(LPNMHDR)lParam;
  2309. switch (l->code)
  2310. {
  2311. case TTN_SHOW:
  2312. {
  2313. LVHITTESTINFO lvh = {0};
  2314. GetCursorPos(&lvh.pt);
  2315. ScreenToClient(hwnd, &lvh.pt);
  2316. ListView_SubItemHitTest(hwnd, &lvh);
  2317. int cloudcol = (int)GetPropW(hwnd, L"pmp_list_info");
  2318. if (lvh.iItem != -1 && lvh.iSubItem == cloudcol)
  2319. {
  2320. RECT r = {0};
  2321. if (lvh.iSubItem)
  2322. ListView_GetSubItemRect(hwnd, lvh.iItem, lvh.iSubItem, LVIR_BOUNDS, &r);
  2323. else
  2324. {
  2325. ListView_GetItemRect(hwnd, lvh.iItem, &r, LVIR_BOUNDS);
  2326. r.right = r.left + ListView_GetColumnWidth(hwnd, cloudcol);
  2327. }
  2328. MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)&r, 2);
  2329. SetWindowPos(l->hwndFrom, HWND_TOPMOST, r.right, r.top + 2, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE);
  2330. return 1;
  2331. }
  2332. }
  2333. break;
  2334. case TTN_NEEDTEXTW:
  2335. {
  2336. LVHITTESTINFO lvh = {0};
  2337. GetCursorPos(&lvh.pt);
  2338. ScreenToClient(hwnd, &lvh.pt);
  2339. ListView_SubItemHitTest(hwnd, &lvh);
  2340. int cloudcol = (int)GetPropW(hwnd, L"pmp_list_info");
  2341. if (lvh.iItem != -1 && lvh.iSubItem == cloudcol)
  2342. {
  2343. LPNMTTDISPINFOW lpnmtdi = (LPNMTTDISPINFOW)lParam;
  2344. int icon = 4;
  2345. wchar_t *t = getRecordExtendedItem_fast(&itemCache.Items[lvh.iItem], extended_fields.cloud);
  2346. if (t && *t) icon = _wtoi(t);
  2347. if (last_item == lvh.iItem && last_icon == icon)
  2348. {
  2349. lpnmtdi->lpszText = tt_buf;
  2350. return 0;
  2351. }
  2352. if (icon == 4)
  2353. {
  2354. WASABI_API_LNGSTRINGW_BUF(IDS_UPLOAD_TO_SOURCE, tt_buf, ARRAYSIZE(tt_buf));
  2355. }
  2356. else if (icon == 5)
  2357. {
  2358. WASABI_API_LNGSTRINGW_BUF(IDS_UPLOADING_TO_SOURCE, tt_buf, ARRAYSIZE(tt_buf));
  2359. }
  2360. else
  2361. {
  2362. if (!cloud_hinst) cloud_hinst = (HINSTANCE)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_HINST);
  2363. if (cloud_hinst && cloud_hinst != (HINSTANCE)1)
  2364. {
  2365. winampMediaLibraryPlugin *(*gp)();
  2366. gp = (winampMediaLibraryPlugin * (__cdecl *)(void))GetProcAddress(cloud_hinst, "winampGetMediaLibraryPlugin");
  2367. if (gp)
  2368. {
  2369. winampMediaLibraryPlugin *mlplugin = gp();
  2370. if (mlplugin && (mlplugin->version >= MLHDR_VER_OLD && mlplugin->version <= MLHDR_VER))
  2371. {
  2372. WASABI_API_LNGSTRINGW_BUF(IDS_TRACK_AVAILABLE, tt_buf, ARRAYSIZE(tt_buf));
  2373. nx_string_t *out_devicenames = 0;
  2374. size_t num_names = mlplugin->MessageProc(0x405, (INT_PTR)itemCache.Items[lvh.iItem].filename, (INT_PTR)&out_devicenames, 0);
  2375. if (num_names > 0)
  2376. {
  2377. for (size_t i = 0; i < num_names; i++)
  2378. {
  2379. if (i > 0) StringCchCatW(tt_buf, ARRAYSIZE(tt_buf), L", ");
  2380. StringCchCatW(tt_buf, ARRAYSIZE(tt_buf), out_devicenames[i]->string);
  2381. }
  2382. }
  2383. else
  2384. {
  2385. WASABI_API_LNGSTRINGW_BUF(IDS_UPLOAD_TO_SOURCE, tt_buf, ARRAYSIZE(tt_buf));
  2386. }
  2387. if (out_devicenames)
  2388. free(out_devicenames);
  2389. }
  2390. }
  2391. }
  2392. }
  2393. last_item = lvh.iItem;
  2394. last_icon = icon;
  2395. lpnmtdi->lpszText = tt_buf;
  2396. // bit of a fiddle but it allows for multi-line tooltips
  2397. //SendMessage(l->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 0);
  2398. }
  2399. else
  2400. return CallWindowProcW((WNDPROC)GetPropW(hwnd, L"pmp_list_proc"), hwnd, uMsg, wParam, lParam);
  2401. }
  2402. return 0;
  2403. }
  2404. }
  2405. return CallWindowProcW((WNDPROC)GetPropW(hwnd, L"pmp_list_proc"), hwnd, uMsg, wParam, lParam);
  2406. }
  2407. void Dialog_UpdateButtonText(HWND hwndDlg, int _enqueuedef)
  2408. {
  2409. if (groupBtn)
  2410. {
  2411. switch(_enqueuedef)
  2412. {
  2413. case 1:
  2414. SetDlgItemTextW(hwndDlg, IDC_BUTTON_PLAY, view.enqueue);
  2415. customAllowed = FALSE;
  2416. break;
  2417. default:
  2418. // v5.66+ - re-use the old predixis parts so the button can be used functionally via ml_enqplay
  2419. // pass the hwnd, button id and plug-in id so the ml plug-in can check things as needed
  2420. pluginMessage p = {ML_MSG_VIEW_BUTTON_HOOK_IN_USE, (INT_PTR)_enqueuedef, 0, 0};
  2421. wchar_t *pszTextW = (wchar_t *)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p);
  2422. if (pszTextW && pszTextW[0] != 0)
  2423. {
  2424. // set this to be a bit different so we can just use one button and not the
  2425. // mixable one as well (leaving that to prevent messing with the resources)
  2426. SetDlgItemTextW(hwndDlg, IDC_BUTTON_PLAY, pszTextW);
  2427. customAllowed = TRUE;
  2428. }
  2429. else
  2430. {
  2431. SetDlgItemTextW(hwndDlg, IDC_BUTTON_PLAY, view.play);
  2432. customAllowed = FALSE;
  2433. }
  2434. break;
  2435. }
  2436. }
  2437. }
  2438. enum
  2439. {
  2440. BPM_ECHO_WM_COMMAND=0x1, // send WM_COMMAND and return value
  2441. BPM_WM_COMMAND = 0x2, // just send WM_COMMAND
  2442. };
  2443. BOOL Dialog_ButtonPopupMenu(HWND hwndDlg, int buttonId, HMENU menu, int flags=0)
  2444. {
  2445. RECT r;
  2446. HWND buttonHWND = GetDlgItem(hwndDlg, buttonId);
  2447. GetWindowRect(buttonHWND, &r);
  2448. UpdateMenuItems(hwndDlg, menu, IDR_VIEW_ACCELERATORS);
  2449. MLSkinnedButton_SetDropDownState(buttonHWND, TRUE);
  2450. UINT tpmFlags = TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_BOTTOMALIGN | TPM_LEFTALIGN;
  2451. if (!(flags & BPM_WM_COMMAND)) tpmFlags |= TPM_RETURNCMD;
  2452. int x = DoTrackPopup(menu, tpmFlags, r.left, r.top, hwndDlg, NULL);
  2453. if ((flags & BPM_ECHO_WM_COMMAND) && x)
  2454. SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(x, 0), 0);
  2455. MLSkinnedButton_SetDropDownState(buttonHWND, FALSE);
  2456. return x;
  2457. }
  2458. static void Dialog_Play(HWND hwndDlg, HWND from, UINT idFrom)
  2459. {
  2460. HMENU listMenu = GetSubMenu(g_context_menus2, 0);
  2461. int count = GetMenuItemCount(listMenu);
  2462. if (count > 2)
  2463. {
  2464. for (int i = 2; i < count; i++)
  2465. {
  2466. DeleteMenu(listMenu, 2, MF_BYPOSITION);
  2467. }
  2468. }
  2469. Dialog_ButtonPopupMenu(hwndDlg, idFrom, listMenu, BPM_WM_COMMAND);
  2470. }
  2471. static INT_PTR Dialog_OnInit(HWND hwndDlg, HWND hwndFocus, LPARAM lParam)
  2472. {
  2473. // benski> this is just going here because it's very likely to get called. will only get compiled in debug mode.
  2474. assert(sizeof(extra_idsW) / sizeof(*extra_idsW) == sizeof(extra_strsW) / sizeof(*extra_strsW));
  2475. g_displaysearch = !(BOOL)lParam;
  2476. // Set the hwnd for the search to a global only if the multipane view hasnt set it yet, as it takes precedence to which box gets populated with the query
  2477. //if (!IsWindow(hwndSearchGlobal))
  2478. hwndSearchGlobal = GetDlgItem(hwndDlg, IDC_QUICKSEARCH);
  2479. HACCEL accel = WASABI_API_LOADACCELERATORSW(IDR_VIEW_ACCELERATORS);
  2480. if (accel)
  2481. WASABI_API_APP->app_addAccelerators(hwndDlg, &accel, 1, TRANSLATE_MODE_CHILD);
  2482. if (!view.play)
  2483. {
  2484. SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_GET_VIEW_BUTTON_TEXT, (WPARAM)&view);
  2485. }
  2486. HWND hwndList;
  2487. FLICKERFIX ff;
  2488. INT index;
  2489. INT ffcl[] = { IDC_CLEAR,
  2490. IDC_BUTTON_PLAY,
  2491. IDC_BUTTON_ENQUEUE,
  2492. IDC_BUTTON_MIX,
  2493. IDC_BUTTON_INFOTOGGLE,
  2494. IDC_BUTTON_CREATEPLAYLIST,
  2495. IDC_MIXABLE,
  2496. IDC_MEDIASTATUS,
  2497. };
  2498. m_hwnd = hwndDlg;
  2499. m_bgupdinfoviewerflag = 0;
  2500. columnOrder[0] = -1;
  2501. last_item = -1;
  2502. tt_buf[0] = 0;
  2503. if (!cloud_hinst) cloud_hinst = (HINSTANCE)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_HINST);
  2504. if (cloud_hinst && cloud_hinst != (HINSTANCE)1)
  2505. {
  2506. winampMediaLibraryPlugin *(*gp)();
  2507. gp = (winampMediaLibraryPlugin * (__cdecl *)(void))GetProcAddress(cloud_hinst, "winampGetMediaLibraryPlugin");
  2508. if (gp)
  2509. {
  2510. winampMediaLibraryPlugin *mlplugin = gp();
  2511. if (mlplugin && (mlplugin->version >= MLHDR_VER_OLD && mlplugin->version <= MLHDR_VER))
  2512. {
  2513. int64_t *out_ids = 0;
  2514. nx_string_t *out_filenames = 0;
  2515. size_t num_files = mlplugin->MessageProc(0x404, (INT_PTR)&out_filenames, (INT_PTR)&out_ids, 0xDEADBEEF);
  2516. for (size_t i = 0; i < num_files; i++)
  2517. {
  2518. cloudFiles.push_back((wchar_t *)out_filenames[i]->string);
  2519. }
  2520. if (out_filenames)
  2521. {
  2522. free(out_filenames);
  2523. out_filenames = 0;
  2524. }
  2525. if (out_ids)
  2526. {
  2527. free(out_ids);
  2528. out_ids = 0;
  2529. }
  2530. HWND ml_pmp_window = FindWindowW(L"ml_pmp_window", NULL);
  2531. if (IsWindow(ml_pmp_window))
  2532. {
  2533. SendMessage(ml_pmp_window, WM_PMP_IPC, (WPARAM)&cloudUploading, PMP_IPC_GETCLOUDTRANSFERS);
  2534. wchar_t a[32] = {0};
  2535. StringCchPrintfW(a, 32, L"%d", cloudUploading.size());
  2536. }
  2537. }
  2538. }
  2539. }
  2540. EnterCriticalSection(&g_db_cs);
  2541. if (!m_media_scanner) m_media_scanner = NDE_Table_CreateScanner(g_table);
  2542. LeaveCriticalSection(&g_db_cs);
  2543. itemCache.Items = 0;
  2544. itemCache.Alloc = 0;
  2545. itemCache.Size = 0;
  2546. hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
  2547. if (IsWindow(hwndList))
  2548. {
  2549. resultlist.setwnd(hwndList);
  2550. resultlist.ForceUnicode();
  2551. DWORD styleEx = LVS_EX_DOUBLEBUFFER | LVS_EX_HEADERDRAGDROP | LVS_EX_ONECLICKACTIVATE; /*LVS_EX_ONECLICKACTIVATE - needed to hottracking work prior WinXp */
  2552. SendMessageW(hwndList, LVM_SETEXTENDEDLISTVIEWSTYLE, styleEx, styleEx);
  2553. HWND hwndHeader = (HWND)SendMessage(hwndList, LVM_GETHEADER, 0, 0L);
  2554. if (IsWindow(hwndHeader)) SetWindowLongPtrW(hwndHeader, GWLP_ID, IDC_LIST2HEADER);
  2555. MLSKINWINDOW skin = {0};
  2556. skin.hwndToSkin = hwndList;
  2557. skin.skinType = SKINNEDWND_TYPE_LISTVIEW;
  2558. skin.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWLVS_FULLROWSELECT | SWLVS_DOUBLEBUFFER | SWLVS_ALTERNATEITEMS;
  2559. MLSkinWindow(plugin.hwndLibraryParent, &skin);
  2560. }
  2561. else resultlist.setwnd(NULL);
  2562. ff.mode = FFM_ERASEINPAINT;
  2563. for (index = 0; index < sizeof(ffcl) / sizeof(INT); index++)
  2564. {
  2565. ff.hwnd = GetDlgItem(hwndDlg, ffcl[index]);
  2566. SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_FLICKERFIX, (WPARAM)&ff);
  2567. }
  2568. if (!g_displaysearch) // disable search box
  2569. {
  2570. ShowWindow(GetDlgItem(hwndDlg, IDC_QUICKSEARCH), SW_HIDE);
  2571. ShowWindow(GetDlgItem(hwndDlg, IDC_CLEAR), SW_HIDE);
  2572. ShowWindow(GetDlgItem(hwndDlg, IDC_SEARCHCAPTION), SW_HIDE);
  2573. }
  2574. if (!cloud_hinst || cloud_hinst == (HINSTANCE)1)
  2575. memcpy(columnOrder, defColumnOrder, sizeof(defColumnOrder));
  2576. else
  2577. memcpy(columnOrder, defColumnOrderCloud, sizeof(defColumnOrderCloud));
  2578. //Read the column order
  2579. Dialog_OnDisplayChange(hwndDlg);
  2580. int cloudcol = -1;
  2581. int l = g_view_metaconf->ReadInt(L"nbcolumns", 0);
  2582. if (l)
  2583. {
  2584. if (l > MAX_COLUMN_ORDER - 1) l = MAX_COLUMN_ORDER - 1;
  2585. index = 0;
  2586. for (; index < l; index++)
  2587. {
  2588. wchar_t tmp[128] = {0};
  2589. StringCchPrintfW(tmp, 128, L"column%d", index);
  2590. int v = g_view_metaconf->ReadInt(tmp, 0);
  2591. if (v == MEDIAVIEW_COL_CLOUD) cloudcol = index;
  2592. if (v < 0 || v >= MEDIAVIEW_COL_NUMS) v = 0;
  2593. columnOrder[index] = (BYTE)v;
  2594. }
  2595. columnOrder[index] = -1;
  2596. }
  2597. if (cloudcol == -1 && !g_view_metaconf->ReadInt(L"cloud", 1))
  2598. {
  2599. g_view_metaconf->WriteInt(L"cloud", 1);
  2600. for(int i = l; i != 0; i--)
  2601. {
  2602. columnOrder[i+1] = columnOrder[i];
  2603. if (i == 3)
  2604. {
  2605. columnOrder[i] = MEDIAVIEW_COL_CLOUD;
  2606. break;
  2607. }
  2608. }
  2609. }
  2610. if (!GetPropW(hwndList, L"pmp_list_proc")) {
  2611. SetPropW(hwndList, L"pmp_list_proc", (HANDLE)SetWindowLongPtrW(hwndList, GWLP_WNDPROC, (LONG_PTR)pmp_listview));
  2612. }
  2613. initColumnsHeader(hwndList);
  2614. char *pszTextA = (g_config->ReadInt(L"remembersearch", 0)) ? g_view_metaconf->ReadString("lastquery_utf8", "") : "";
  2615. AutoWide queryUnicode(pszTextA, CP_UTF8);
  2616. SetDlgItemTextW(hwndDlg, IDC_QUICKSEARCH, queryUnicode);
  2617. KillTimer(hwndDlg, UPDATE_QUERY_TIMER_ID);
  2618. doQuery(hwndDlg, queryUnicode, 0);
  2619. updateInfoText(hwndDlg);
  2620. search_oldWndProc = (WNDPROC)SetWindowLongPtrW(GetDlgItem(hwndDlg, IDC_QUICKSEARCH), GWLP_WNDPROC, (LONG_PTR)search_newWndProc);
  2621. if (g_table && !NDE_Table_GetRecordsCount(g_table) && !g_config->ReadInt(L"noshowadddlg", 0))
  2622. {
  2623. SetTimer(hwndDlg, 5050, 1000, NULL);
  2624. }
  2625. groupBtn = g_config->ReadInt(L"groupbtn", 1);
  2626. enqueuedef = (g_config->ReadInt(L"enqueuedef", 0) == 1);
  2627. /// detect predixis
  2628. predixisExist = FALSE;
  2629. //predixis out - begin
  2630. //pluginMessage p = {ML_MSG_PDXS_STATUS, (INT_PTR)"test", 0, 0};
  2631. //pszTextA = (char *)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p);
  2632. //predixisExist = (pszTextA != NULL && pszTextA[0] != 0);
  2633. //predixis out - end
  2634. // v5.66+ - re-use the old predixis parts so the button can be used functionally via ml_enqplay
  2635. // pass the hwnd, button id and plug-in id so the ml plug-in can check things as needed
  2636. pluginMessage p = {ML_MSG_VIEW_BUTTON_HOOK, (INT_PTR)hwndDlg, (INT_PTR)MAKELONG(IDC_BUTTON_MIX, IDC_BUTTON_ENQUEUE), (INT_PTR)L"ml_local"};
  2637. wchar_t *pszTextW = (wchar_t *)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p);
  2638. if (pszTextW && pszTextW[0] != 0)
  2639. {
  2640. // set this to be a bit different so we can just use one button and not the
  2641. // mixable one as well (leaving that to prevent messing with the resources)
  2642. customAllowed = TRUE;
  2643. SetDlgItemTextW(hwndDlg, IDC_BUTTON_MIX, pszTextW);
  2644. }
  2645. else
  2646. customAllowed = FALSE;
  2647. ShowWindow(GetDlgItem(hwndDlg, IDC_BUTTON_MIX), (!customAllowed ? SW_HIDE : SW_SHOW));
  2648. ShowWindow(GetDlgItem(hwndDlg, IDC_MIXABLE), (!(predixisExist & 1) ? SW_HIDE : SW_SHOW));
  2649. MLSKINWINDOW m = {0};
  2650. m.hwndToSkin = hwndDlg;
  2651. m.skinType = SKINNEDWND_TYPE_AUTO;
  2652. m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWLVS_FULLROWSELECT | SWLVS_DOUBLEBUFFER | SWLVS_ALTERNATEITEMS;
  2653. MLSkinWindow(plugin.hwndLibraryParent, &m);
  2654. m.skinType = SKINNEDWND_TYPE_BUTTON;
  2655. m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | (groupBtn ? SWBS_SPLITBUTTON : 0);
  2656. const int buttonids[] = {IDC_BUTTON_PLAY, IDC_BUTTON_ENQUEUE, IDC_BUTTON_MIX};
  2657. for (size_t i=0;i!=sizeof(buttonids)/sizeof(buttonids[0]);i++)
  2658. {
  2659. m.hwndToSkin = GetDlgItem(hwndDlg, buttonids[i]);
  2660. if (IsWindow(m.hwndToSkin)) MLSkinWindow(plugin.hwndLibraryParent, &m);
  2661. }
  2662. m.skinType = SKINNEDWND_TYPE_AUTO;
  2663. m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS;
  2664. const int buttonidz[] = {IDC_SEARCHCAPTION, IDC_QUICKSEARCH, IDC_MEDIASTATUS, IDC_CLEAR, IDC_BUTTON_INFOTOGGLE, IDC_BUTTON_CREATEPLAYLIST, IDC_MIXABLE};
  2665. for (size_t i=0;i!=sizeof(buttonidz)/sizeof(buttonidz[0]);i++)
  2666. {
  2667. m.hwndToSkin = GetDlgItem(hwndDlg, buttonidz[i]);
  2668. if (IsWindow(m.hwndToSkin)) MLSkinWindow(plugin.hwndLibraryParent, &m);
  2669. }
  2670. Dialog_UpdateButtonText(hwndDlg, enqueuedef);
  2671. return FALSE;
  2672. }
  2673. static BOOL Dialog_OnNotify(HWND hwndDlg, INT idCtrl, NMHDR* pnmh, LRESULT *pResult)
  2674. {
  2675. switch (pnmh->idFrom)
  2676. {
  2677. case IDC_LIST2:
  2678. switch (pnmh->code)
  2679. {
  2680. case LVN_ITEMCHANGED: return ListView_OnItemChanged(hwndDlg, (NMLISTVIEW*)pnmh);
  2681. case NM_DBLCLK: return ListView_OnDoubleClick(hwndDlg, (NMITEMACTIVATE*)pnmh);
  2682. case LVN_ODFINDITEMA:
  2683. case LVN_ODFINDITEMW: return ListView_OnFindItem(hwndDlg, (NMLVFINDITEMW*)pnmh, pResult, pnmh->code);
  2684. case LVN_GETDISPINFOA:
  2685. case LVN_GETDISPINFOW: return ListView_OnGetDispInfo(hwndDlg, (NMLVDISPINFOW*)pnmh, pnmh->code);
  2686. case LVN_COLUMNCLICK: return ListView_OnColumnClick(hwndDlg, (NMLISTVIEW*)pnmh);
  2687. case LVN_BEGINDRAG: return ListView_OnBeginDrag(hwndDlg, (NMLISTVIEW*)pnmh);
  2688. case NM_RETURN: return ListView_OnReturn(hwndDlg, pnmh);
  2689. case NM_CUSTOMDRAW: return ListView_OnCustomDraw(hwndDlg, (NMLVCUSTOMDRAW*)pnmh, pResult);
  2690. case LVN_HOTTRACK: return ListView_OnHotTrack(hwndDlg, (NMLISTVIEW*)pnmh, pResult);
  2691. case NM_CLICK: return ListView_OnClick(hwndDlg, (NMITEMACTIVATE*)pnmh);
  2692. }
  2693. break;
  2694. case IDC_LIST2HEADER:
  2695. switch (pnmh->code)
  2696. {
  2697. case NM_RCLICK: return Header_OnRightClick(hwndDlg, pnmh, pResult);
  2698. case HDN_ENDDRAG: return Header_OnEndDrag(hwndDlg, (NMHEADERW*)pnmh, pResult);
  2699. case HDN_ITEMCHANGINGA:
  2700. case HDN_ITEMCHANGINGW: return Header_OnItemChanging(hwndDlg, (NMHEADERW*)pnmh, pResult, pnmh->code);
  2701. }
  2702. break;
  2703. }
  2704. return FALSE;
  2705. }
  2706. static void Dialog_OnInitMenuPopup(HWND hwndDlg, HMENU hMenu, UINT nIndex, BOOL bSysMenu)
  2707. {
  2708. if (hMenu && hMenu == s.build_hMenu && s.mode == 1)
  2709. {
  2710. myMenu = TRUE;
  2711. if (SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
  2712. s.mode = 2;
  2713. myMenu = FALSE;
  2714. }
  2715. if (rate_hmenu && hMenu == rate_hmenu)
  2716. {
  2717. int x;
  2718. int sel = 0;
  2719. for (x = 0; x < itemCache.Size; x ++)
  2720. {
  2721. if (resultlist.GetSelected(x))
  2722. {
  2723. int s = itemCache.Items[x].rating;
  2724. if (s == sel || !sel) sel = s;
  2725. if (s != sel) break;
  2726. }
  2727. }
  2728. if (-1 == sel) sel = 0;
  2729. Menu_SetRatingValue(rate_hmenu, sel);
  2730. }
  2731. if (cloud_hmenu && hMenu == cloud_hmenu)
  2732. {
  2733. int n = resultlist.GetSelectionMark();
  2734. if (n != -1 && !GetMenuItemCount(hMenu))
  2735. {
  2736. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_GET_CLOUD_STATUS, (intptr_t)itemCache.Items[n].filename, (intptr_t)&cloud_hmenu);
  2737. }
  2738. }
  2739. }
  2740. static void Dialog_OnMouseMove(HWND hwndDlg, UINT nFlags, POINTS pts)
  2741. {
  2742. if (GetCapture() == hwndDlg)
  2743. {
  2744. mlDropItemStruct m = {0};
  2745. POINTSTOPOINT(m.p, pts);
  2746. MapWindowPoints(hwndDlg, HWND_DESKTOP, (POINT*)&m.p, 1);
  2747. if (MLRatingColumn_Drag(plugin.hwndLibraryParent, &m.p)) return;
  2748. m.type = ML_TYPE_ITEMRECORDLIST;
  2749. pluginHandleIpcMessage(ML_IPC_HANDLEDRAG, (WPARAM)&m);
  2750. }
  2751. }
  2752. static void Dialog_OnLButtonUp(HWND hwndDlg, UINT nFlags, POINTS pts)
  2753. {
  2754. if (GetCapture() == hwndDlg)
  2755. {
  2756. mlDropItemStruct m = {0};
  2757. ReleaseCapture();
  2758. POINTSTOPOINT(m.p, pts);
  2759. MapWindowPoints(hwndDlg, HWND_DESKTOP, (POINT*)&m.p, 1);
  2760. ratingColumn.bCanceled = FALSE;
  2761. ratingColumn.ptAction = m.p;
  2762. ratingColumn.bRedrawNow = TRUE;
  2763. if (MLRatingColumn_EndDrag(plugin.hwndLibraryParent, &ratingColumn))
  2764. {
  2765. SetRating(ratingColumn.iItem, ratingColumn.value, ratingColumn.hwndList);
  2766. return;
  2767. }
  2768. m.type = ML_TYPE_ITEMRECORDLISTW;
  2769. m.flags = ML_HANDLEDRAG_FLAG_NOCURSOR;
  2770. pluginHandleIpcMessage(ML_IPC_HANDLEDRAG, (WPARAM)&m);
  2771. if (m.result > 0) // try itemRecordListW
  2772. {
  2773. itemRecordListW myObj = {0, };
  2774. copyFilesToItemCacheW(&myObj);
  2775. m.flags = 0;
  2776. m.result = 0;
  2777. m.data = (void*) & myObj;
  2778. pluginHandleIpcMessage(ML_IPC_HANDLEDROP, (WPARAM)&m);
  2779. _aligned_free(myObj.Items); // DO NOT empty this object, cause it doesnt own its data
  2780. }
  2781. else // if it didn't work, fall back to itemRecordList
  2782. {
  2783. m.type = ML_TYPE_ITEMRECORDLIST;
  2784. m.result = 0;
  2785. pluginHandleIpcMessage(ML_IPC_HANDLEDRAG, (WPARAM)&m);
  2786. if (m.result > 0)
  2787. {
  2788. itemRecordListW myObj = {0, };
  2789. copyFilesToItemCacheW(&myObj);
  2790. itemRecordList objA = {0, };
  2791. convertRecordList(&objA, &myObj);
  2792. m.flags = 0;
  2793. m.result = 0;
  2794. m.data = (void*) & objA;
  2795. pluginHandleIpcMessage(ML_IPC_HANDLEDROP, (WPARAM)&m);
  2796. emptyRecordList(&objA);
  2797. freeRecordList(&objA);
  2798. _aligned_free(myObj.Items); // DO NOT empty this object, cause it doesnt own its data
  2799. }
  2800. }
  2801. }
  2802. }
  2803. class ItemRecordPlaylist : public ifc_playlist
  2804. {
  2805. public:
  2806. ItemRecordPlaylist(const itemRecordListW *_list)
  2807. {
  2808. list = _list;
  2809. }
  2810. private:
  2811. size_t GetNumItems()
  2812. {
  2813. return list->Size;
  2814. }
  2815. size_t GetItem(size_t item, wchar_t *filename, size_t filenameCch)
  2816. {
  2817. if (item < (size_t)list->Size && list->Items[item].filename)
  2818. {
  2819. StringCchCopyW(filename, filenameCch, list->Items[item].filename);
  2820. return 1;
  2821. }
  2822. return 0;
  2823. }
  2824. size_t GetItemTitle(size_t item, wchar_t *title, size_t titleCch)
  2825. {
  2826. if (item < (size_t)list->Size && list->Items[item].filename)
  2827. {
  2828. TAG_FMT_EXT(list->Items[item].filename, itemrecordWTagFunc, ndeTagFuncFree, (void*)&list->Items[item], title, titleCch, 0);
  2829. return 1;
  2830. }
  2831. return 0;
  2832. }
  2833. int GetItemLengthMilliseconds(size_t item)
  2834. {
  2835. if (item < (size_t)list->Size && list->Items[item].length>=0)
  2836. {
  2837. return list->Items[item].length * 1000;
  2838. }
  2839. return -1000;
  2840. }
  2841. private:
  2842. const itemRecordListW *list;
  2843. protected:
  2844. RECVS_DISPATCH;
  2845. };
  2846. #define CBCLASS ItemRecordPlaylist
  2847. START_DISPATCH;
  2848. CB(IFC_PLAYLIST_GETNUMITEMS, GetNumItems)
  2849. CB(IFC_PLAYLIST_GETITEM, GetItem)
  2850. CB(IFC_PLAYLIST_GETITEMTITLE, GetItemTitle)
  2851. CB(IFC_PLAYLIST_GETITEMLENGTHMILLISECONDS, GetItemLengthMilliseconds)
  2852. END_DISPATCH;
  2853. #undef CBCLASS
  2854. static void Dialog_OnCommand(HWND hwndDlg, UINT idCtrl, INT nCode, HWND hwndCtrl)
  2855. {
  2856. if (GetFocus() != hwndSearchGlobal)
  2857. {
  2858. switch (idCtrl)
  2859. {
  2860. case IDC_CLEAR:
  2861. SetDlgItemText(hwndDlg, IDC_QUICKSEARCH, L"");
  2862. break;
  2863. case IDC_BUTTON_INFOTOGGLE:
  2864. updateInfoText(hwndDlg, TRUE);
  2865. UpdateWindow(hwndDlg);
  2866. LayoutWindows(hwndDlg, TRUE);
  2867. break;
  2868. case IDC_QUICKSEARCH:
  2869. if (nCode == EN_CHANGE)
  2870. {
  2871. KillTimer(hwndDlg, UPDATE_QUERY_TIMER_ID);
  2872. SetTimer(hwndDlg, UPDATE_QUERY_TIMER_ID, g_querydelay, NULL);
  2873. }
  2874. break;
  2875. case ID_AUDIOWND_PLAYSELECTION:
  2876. case ID_QUERYWND_PLAYQUERY:
  2877. case IDC_BUTTON_PLAY:
  2878. case ID_MEDIAWND_PLAYSELECTEDFILES:
  2879. case ID_AUDIOWND_ENQUEUESELECTION:
  2880. case IDC_BUTTON_ENQUEUE:
  2881. case ID_MEDIAWND_ENQUEUESELECTEDFILES:
  2882. case IDC_BUTTON_MIX:
  2883. {
  2884. if (nCode == MLBN_DROPDOWN)
  2885. {
  2886. Dialog_Play(hwndDlg, hwndCtrl, idCtrl);
  2887. }
  2888. else
  2889. {
  2890. int action;
  2891. if (idCtrl == IDC_BUTTON_PLAY || idCtrl == ID_MEDIAWND_PLAYSELECTEDFILES || idCtrl == ID_AUDIOWND_PLAYSELECTION)
  2892. {
  2893. action = (nCode == 1) ? g_config->ReadInt(L"enqueuedef", 0) == 1 : 0;
  2894. }
  2895. else if (idCtrl == IDC_BUTTON_ENQUEUE || idCtrl == ID_MEDIAWND_ENQUEUESELECTEDFILES || idCtrl == ID_AUDIOWND_ENQUEUESELECTION)
  2896. {
  2897. action = (nCode == 1) ? g_config->ReadInt(L"enqueuedef", 0) != 1 : 1;
  2898. }
  2899. else
  2900. break;
  2901. int i, l = itemCache.Size;
  2902. for (i = 0; i < l; i++) if (resultlist.GetSelected(i)) break;
  2903. playFiles(action/*idCtrl == IDC_BUTTON_ENQUEUE*/, i == l);
  2904. }
  2905. }
  2906. break;
  2907. case IDC_BUTTON_CREATEPLAYLIST:
  2908. #if 0
  2909. // TODO consider exposing this option somehow...
  2910. if (AGAVE_API_PLAYLISTMANAGER) // This is the old Create Playlist button code
  2911. {
  2912. wchar_t fn[MAX_PATH] = {0};
  2913. wchar_t dir[MAX_PATH] = {0};
  2914. GetTempPathW(MAX_PATH,dir);
  2915. GetTempFileNameW(dir,L"ml_playlist",0,fn);
  2916. wcscat(fn,L".m3u8");
  2917. ItemRecordPlaylist playlist(&itemCache);
  2918. if (AGAVE_API_PLAYLISTMANAGER->Save(fn, &playlist) == PLAYLISTMANAGER_SUCCESS)
  2919. {
  2920. mlAddPlaylist p={sizeof(p),NULL,fn,PL_FLAGS_IMPORT,-1,-1};
  2921. SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&p,ML_IPC_PLAYLIST_ADD);
  2922. DeleteFileW(fn);
  2923. }
  2924. }
  2925. #endif
  2926. if (AGAVE_API_PLAYLIST_GENERATOR)
  2927. {
  2928. const int number_selected = resultlist.GetSelectedCount(); // Total selected
  2929. if (number_selected > 0)
  2930. {
  2931. itemRecordListW recordList; // Record list
  2932. itemRecordW *records = new itemRecordW[number_selected]; // Array of records
  2933. int selectedRecordcounter = 0;
  2934. for (int i = 0; i < itemCache.Size; i++)
  2935. {
  2936. if (resultlist.GetSelected(i)) // See if the current item is selected or not
  2937. {
  2938. records[selectedRecordcounter] = itemCache.Items[i]; // If its selected then add it to our itemlist
  2939. selectedRecordcounter++;
  2940. }
  2941. }
  2942. recordList.Size = selectedRecordcounter; // Set the correct size of the record list
  2943. recordList.Items = records; // Set the array of records to the record list
  2944. AGAVE_API_PLAYLIST_GENERATOR->GeneratePlaylist(hwndDlg, &recordList); // Call the playlist API with the list of selected records
  2945. delete [] records; // Free up the array of records
  2946. }
  2947. else
  2948. {
  2949. wchar_t title[64] = {0};
  2950. MessageBoxW(m_hwnd, WASABI_API_LNGSTRINGW(IDS_ERROR_PLG_SELECT_TRACKS), WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_PLAYLIST_GENERATOR,title,64), MB_OK | MB_ICONINFORMATION);
  2951. }
  2952. }
  2953. break;
  2954. #if 0
  2955. case IDC_BUTTON_MIX:
  2956. {
  2957. itemRecordList list;
  2958. int i;
  2959. int ct = 0;
  2960. for (i = 0; i < itemCache.Size; i++)
  2961. {
  2962. if (resultlist.GetSelected(i))
  2963. {
  2964. ct++;
  2965. }
  2966. }
  2967. ZeroMemory(&list, sizeof(itemRecordList));
  2968. allocRecordList(&list, ct, 1);
  2969. list.Size = ct;
  2970. ct = 0;
  2971. for (i = 0; i < itemCache.Size; i++)
  2972. {
  2973. if (resultlist.GetSelected(i))
  2974. {
  2975. convertRecord(&list.Items[ct], &itemCache.Items[i]);
  2976. ct++;
  2977. }
  2978. }
  2979. pluginMessage p = {ML_MSG_PDXS_MIX, (INT_PTR) &list, 0, 0};
  2980. SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&p, ML_IPC_SEND_PLUGIN_MESSAGE);
  2981. emptyRecordList(&list);
  2982. freeRecordList(&list);
  2983. }
  2984. break;
  2985. #endif
  2986. case ID_MEDIAWND_SELECTALL:
  2987. {
  2988. LVITEM item = {0};
  2989. item.state = LVIS_SELECTED;
  2990. item.stateMask = LVIS_SELECTED;
  2991. SendMessageW(resultlist.getwnd(), LVM_SETITEMSTATE, (WPARAM)-1, (LPARAM)&item);
  2992. }
  2993. break;
  2994. case ID_MEDIAWND_REMOVEFROMLIBRARY:
  2995. removeSelectedItems(0);
  2996. break;
  2997. case ID_EDITITEMINFOS:
  2998. if (resultlist.GetSelectedCount() > 0)
  2999. editInfo(hwndDlg);
  3000. break;
  3001. case ID_PE_ID3:
  3002. fileInfoDialogs(hwndDlg);
  3003. PostMessageW(hwndDlg, WM_NEXTDLGCTL, (WPARAM)resultlist.getwnd(), (LPARAM)TRUE);
  3004. break;
  3005. case IDC_REFRESH_METADATA:
  3006. RefreshMetadata(hwndDlg);
  3007. break;
  3008. case ID_MEDIAWND_EXPLOREFOLDER:
  3009. exploreItemFolder(hwndDlg);
  3010. break;
  3011. }
  3012. }
  3013. else
  3014. {
  3015. switch (idCtrl)
  3016. {
  3017. case IDC_QUICKSEARCH:
  3018. if (nCode == EN_CHANGE)
  3019. {
  3020. KillTimer(hwndDlg, UPDATE_QUERY_TIMER_ID);
  3021. SetTimer(hwndDlg, UPDATE_QUERY_TIMER_ID, g_querydelay, NULL);
  3022. }
  3023. break;
  3024. case ID_MEDIAWND_SELECTALL:
  3025. SendMessageW(hwndSearchGlobal, EM_SETSEL, 0, -1);
  3026. break;
  3027. case ID_MEDIAWND_REMOVEFROMLIBRARY:
  3028. {
  3029. DWORD start = -1, end = -1;
  3030. SendMessageW(hwndSearchGlobal, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
  3031. if (start != -1)
  3032. {
  3033. if (start == end)
  3034. {
  3035. SendMessageW(hwndSearchGlobal, EM_SETSEL, start, end + 1);
  3036. }
  3037. SendMessageW(hwndSearchGlobal, EM_REPLACESEL, TRUE, (LPARAM)"");
  3038. SendMessageW(hwndSearchGlobal, EM_SETSEL, start, start);
  3039. }
  3040. }
  3041. break;
  3042. }
  3043. }
  3044. }
  3045. static void Dialog_OnTimer(HWND hwndDlg, UINT_PTR idEvent, TIMERPROC fnTimer)
  3046. {
  3047. switch (idEvent)
  3048. {
  3049. case 5050:
  3050. KillTimer(hwndDlg, 5050);
  3051. WASABI_API_DIALOGBOXW(IDD_NEEDADDFILES, hwndDlg, needAddFilesProc);
  3052. PostMessage(GetParent(hwndDlg), WM_APP + 1, (WPARAM)0, (LPARAM)0);
  3053. break;
  3054. case 6600:
  3055. KillTimer(hwndDlg, idEvent);
  3056. if (m_last_selitem >= 0 && m_last_selitem < itemCache.Size)
  3057. {
  3058. if (predixisExist & 1)
  3059. {
  3060. // Only single seeds are supported currently
  3061. if (resultlist.GetSelectedCount() == 1 && isMixable(itemCache.Items[m_last_selitem]))
  3062. {
  3063. SetDlgItemTextW(hwndDlg, IDC_MIXABLE, WASABI_API_LNGSTRINGW(IDS_MIXABLE));
  3064. isMixablePresent = true;
  3065. }
  3066. else SetDlgItemText(hwndDlg, IDC_MIXABLE, L"");
  3067. }
  3068. SendMessageW(GetParent(hwndDlg), WM_SHOWFILEINFO, (WPARAM)FALSE, (LPARAM)itemCache.Items[m_last_selitem].filename);
  3069. m_last_selitem = -1;
  3070. }
  3071. break;
  3072. case 123:
  3073. if (bgThread_Handle)
  3074. {
  3075. HWND hwndList;
  3076. hwndList = resultlist.getwnd();
  3077. if (1 != ListView_GetItemCount(hwndList)) ListView_SetItemCountEx(hwndList, 1, LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL);
  3078. ListView_RedrawItems(hwndList, 0, 0);
  3079. //UpdateWindow(hwndList);
  3080. }
  3081. break;
  3082. case UPDATE_QUERY_TIMER_ID:
  3083. {
  3084. KillTimer(hwndDlg, UPDATE_QUERY_TIMER_ID);
  3085. wchar_t buf[2048] = {0};
  3086. GetWindowTextW(GetDlgItem(hwndDlg, IDC_QUICKSEARCH), buf, ARRAYSIZE(buf));
  3087. doQuery(hwndDlg, buf);
  3088. }
  3089. break;
  3090. case UPDATE_RESULT_LIST_TIMER_ID:
  3091. {
  3092. ListView_RedrawItems(resultlist.getwnd(), 0, resultlist.GetCount() - 1);
  3093. }
  3094. break;
  3095. }
  3096. }
  3097. static void Dialog_OnDestroy(HWND hwndDlg)
  3098. {
  3099. HWND hwndList;
  3100. INT i, j;
  3101. wchar_t buf[2048] = {0};
  3102. bgQuery_Stop();
  3103. GetDlgItemTextW(hwndDlg, IDC_QUICKSEARCH, buf, ARRAYSIZE(buf));
  3104. g_view_metaconf->WriteString("lastquery_utf8", AutoChar(buf, CP_UTF8));
  3105. hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
  3106. if (hwndList && IsWindow(hwndList))
  3107. {
  3108. for (i = 0; columnOrder[i] != -1; i++)
  3109. {
  3110. headerColumn *cl = &columnList[columnOrder[i]];
  3111. g_view_metaconf->WriteInt(AutoWide(cl->config_name), SendMessageW(hwndList, LVM_GETCOLUMNWIDTH, i, 0L));
  3112. }
  3113. }
  3114. //Save the column order
  3115. for (i = 0; columnOrder[i] != -1; i++);
  3116. g_view_metaconf->WriteInt(L"nbcolumns", i);
  3117. for (j = 0; j < i; j++)
  3118. {
  3119. wchar_t tmp[128] = {0};
  3120. StringCchPrintfW(tmp, 128, L"column%d", j);
  3121. g_view_metaconf->WriteInt(tmp, columnOrder[j]);
  3122. }
  3123. freeRecordList(&itemCache);
  3124. itemCache.Items = 0;
  3125. itemCache.Alloc = 0;
  3126. itemCache.Size = 0;
  3127. cloudFiles.clear();
  3128. cloudUploading.clear();
  3129. hwndSearchGlobal = 0; // Set the hwnd for the search to a global to null so we know we are not in the single pane view
  3130. }
  3131. static void Dialog_OnWindowPosChanged(HWND hwndDlg, WINDOWPOS *pwp)
  3132. {
  3133. if ((SWP_NOSIZE | SWP_NOMOVE) != ((SWP_NOSIZE | SWP_NOMOVE) & pwp->flags) || (SWP_FRAMECHANGED & pwp->flags))
  3134. {
  3135. LayoutWindows(hwndDlg, !(SWP_NOREDRAW & pwp->flags), 0 != (SWP_SHOWWINDOW & pwp->flags));
  3136. }
  3137. }
  3138. static void Dialog_OnSyncHeaderOrder(HWND hwndDlg, HWND hwndHeader)
  3139. {
  3140. LVCOLUMNW column = {0};
  3141. wchar_t buffer[128] = {0};
  3142. signed char tempOrder[MAX_COLUMN_ORDER] = {0};
  3143. HWND hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
  3144. if (!hwndList) return;
  3145. CopyMemory(tempOrder, columnOrder, sizeof(tempOrder)/sizeof(signed char));
  3146. column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_IMAGE;
  3147. column.cchTextMax = sizeof(buffer) /sizeof(wchar_t);
  3148. SendMessageW(hwndList, WM_SETREDRAW, FALSE, 0L);
  3149. INT sort = MLSkinnedListView_GetSort(hwndList);
  3150. INT count = (INT)SendMessageW(hwndHeader, HDM_GETITEMCOUNT, 0, 0L);
  3151. if (count > 0)
  3152. {
  3153. INT index = count + 1, *pOrder = (INT*)calloc(1, sizeof(INT)*count);
  3154. if (pOrder && SendMessageW(hwndList, LVM_GETCOLUMNORDERARRAY, count, (LPARAM)pOrder))
  3155. {
  3156. INT order;
  3157. for (order = 0; order < count; order++)
  3158. {
  3159. column.pszText = buffer;
  3160. if (!SendMessageW(hwndList, LVM_GETCOLUMNW, pOrder[order], (LPARAM)&column)) continue;
  3161. column.iOrder = order;
  3162. // update position of the cloud column icon
  3163. if (tempOrder[pOrder[order]] == MEDIAVIEW_COL_CLOUD)
  3164. {
  3165. if (!cloud_hinst || cloud_hinst == (HINSTANCE)1 ||
  3166. !SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE))
  3167. {
  3168. MLSkinnedHeader_SetCloudColumn(ListView_GetHeader(hwndList), -1);
  3169. SetPropW(hwndList, L"pmp_list_info", (HANDLE)-1);
  3170. column.cx = 0;
  3171. }
  3172. else
  3173. {
  3174. MLSkinnedHeader_SetCloudColumn(ListView_GetHeader(hwndList), order);
  3175. SetPropW(hwndList, L"pmp_list_info", (HANDLE)order);
  3176. column.cx = 27;
  3177. MLCloudColumn_GetWidth(plugin.hwndLibraryParent, &column.cx);
  3178. }
  3179. }
  3180. SendMessageW(hwndList, LVM_INSERTCOLUMNW, index++, (LPARAM)&column);
  3181. columnOrder[order] = tempOrder[pOrder[order]];
  3182. if (LOWORD(sort) == pOrder[order]) MLSkinnedListView_DisplaySort(hwndList, order, HIWORD(sort));
  3183. }
  3184. for (order = 0; order < count; order++) SendMessageW(hwndList, LVM_DELETECOLUMN, 0, 0L);
  3185. }
  3186. for (index = 0; -1 != columnOrder[index] && MEDIAVIEW_COL_RATING != columnOrder[index] && MEDIAVIEW_COL_CLOUD != columnOrder[index]; index++);
  3187. if (-1 != columnOrder[index])
  3188. {
  3189. INT w = (INT)SendMessageW(hwndList, LVM_GETCOLUMNWIDTH, index, 0L);
  3190. SendMessageW(hwndList, LVM_SETCOLUMNWIDTH, index, (LPARAM)w);
  3191. }
  3192. if (pOrder) free(pOrder);
  3193. }
  3194. SendMessageW(hwndList, WM_SETREDRAW, TRUE, 0L);
  3195. }
  3196. static void Window_OnQueryFileInfo(HWND hwnd)
  3197. {
  3198. INT index;
  3199. HWND hwndList = GetDlgItem(hwnd, IDC_LIST2);
  3200. index = (hwndList) ? (INT)SendMessage(hwndList, LVM_GETNEXTITEM, (WPARAM)-1, (LPARAM)LVNI_FOCUSED) : -1;
  3201. SendMessageW(GetParent(hwnd), WM_SHOWFILEINFO, (WPARAM)FALSE, (LPARAM)((-1 != index && index < itemCache.Size) ? itemCache.Items[index].filename : L""));
  3202. }
  3203. void Window_OnDropFiles(HWND hwndDlg, HDROP hdrop)
  3204. {
  3205. wchar_t temp[1024] = {0};
  3206. int y = DragQueryFileW(hdrop, 0xffffffff, temp, 1024);
  3207. if (y > 0)
  3208. {
  3209. wchar_t **paths = (wchar_t **)calloc(y, sizeof(wchar_t *));
  3210. int *guesses = (int *)calloc(y, sizeof(int));
  3211. int *metas = (int *)calloc(y, sizeof(int));
  3212. int *recs= (int *)calloc(y, sizeof(int));
  3213. if (paths && guesses && metas && recs)
  3214. {
  3215. size_t count=0;
  3216. for (int x = 0; x < y; x ++)
  3217. {
  3218. DragQueryFileW(hdrop, x, temp, 1024);
  3219. int guess = -1, meta = -1, rec = 1;
  3220. // do this for normal media drops
  3221. PLCallBackW plCB;
  3222. if (AGAVE_API_PLAYLISTMANAGER && PLAYLISTMANAGER_SUCCESS != AGAVE_API_PLAYLISTMANAGER->Load(temp, &plCB))
  3223. {
  3224. autoscan_add_directory(temp, &guess, &meta, &rec, 0);
  3225. if (guess == -1) guess = g_config->ReadInt(L"guessmode", 0);
  3226. if (meta == -1) meta = g_config->ReadInt(L"usemetadata", 1);
  3227. paths[count] = _wcsdup(temp);
  3228. guesses[count]=guess;
  3229. metas[count]=meta;
  3230. recs[count]=rec;
  3231. count++;
  3232. }
  3233. }
  3234. DragFinish(hdrop);
  3235. Scan_ScanFolders(hwndDlg, count, paths, guesses, metas, recs);
  3236. if (IsWindow(m_curview_hwnd)) SendMessage(m_curview_hwnd, WM_APP + 1, 0, 0); //update current view
  3237. }
  3238. else
  3239. {
  3240. free(paths);
  3241. free(guesses);
  3242. free(metas);
  3243. free(recs);
  3244. }
  3245. }
  3246. else DragFinish(hdrop);
  3247. }
  3248. INT_PTR CALLBACK view_mediaDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3249. {
  3250. BOOL a = dialogSkinner.Handle(hwndDlg, uMsg, wParam, lParam); if (a) return a;
  3251. switch (uMsg)
  3252. {
  3253. case WM_INITMENUPOPUP: Dialog_OnInitMenuPopup(hwndDlg, (HMENU)wParam, LOWORD(lParam), HIWORD(lParam)); break;
  3254. case WM_DISPLAYCHANGE: Dialog_OnDisplayChange(hwndDlg); break;
  3255. case WM_INITDIALOG: return Dialog_OnInit(hwndDlg, (HWND)wParam, lParam);
  3256. case WM_MOUSEMOVE: Dialog_OnMouseMove(hwndDlg, (UINT)wParam, MAKEPOINTS(lParam)); break;
  3257. case WM_LBUTTONUP: Dialog_OnLButtonUp(hwndDlg, (UINT)wParam, MAKEPOINTS(lParam)); break;
  3258. case WM_DROPFILES: Window_OnDropFiles(hwndDlg, (HDROP)wParam);
  3259. case WM_COMMAND: Dialog_OnCommand(hwndDlg, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); break;
  3260. case WM_TIMER: Dialog_OnTimer(hwndDlg, (UINT_PTR) wParam, (TIMERPROC)lParam); break;
  3261. case WM_DESTROY: Dialog_OnDestroy(hwndDlg); break;
  3262. case WM_WINDOWPOSCHANGED: Dialog_OnWindowPosChanged(hwndDlg, (WINDOWPOS*)lParam); break;
  3263. case WM_ERASEBKGND: return 1; //handled by WADlg_DrawChildWindowBorders in WM_PAINT
  3264. case WM_CONTEXTMENU:
  3265. Dialog_OnContextMenu(hwndDlg, (HWND)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  3266. return 0;
  3267. case WM_NOTIFY:
  3268. {
  3269. LRESULT result;
  3270. result = 0L;
  3271. if (Dialog_OnNotify(hwndDlg, (INT)wParam, (NMHDR*)lParam, &result))
  3272. {
  3273. SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)result);
  3274. return TRUE;
  3275. }
  3276. }
  3277. break;
  3278. case IM_SYNCHEADERORDER: Dialog_OnSyncHeaderOrder(hwndDlg, (HWND)lParam); break;
  3279. case WM_APP + 3: // send by bgthread
  3280. if (wParam == 0x666) m_bgupdinfoviewerflag = 1;
  3281. else if (wParam == 0x69)
  3282. {
  3283. bgQuery_Stop();
  3284. resultlist.SetVirtualCount(0);
  3285. resultlist.SetVirtualCount(itemCache.Size); // TODO: we could set a limit here
  3286. ListView_RedrawItems(resultlist.getwnd(), 0, itemCache.Size - 1);
  3287. UpdateWindow(resultlist.getwnd());
  3288. __int64 total_len_bytes = bg_total_len_bytes;
  3289. int total_length_s = (int)bg_total_len_s & 0x7FFFFFFF;
  3290. wchar_t buffer[4*64] = {0};
  3291. wchar_t *pb[4] = {0};
  3292. for (int i = 0; i < 4; i++) pb[i] = buffer + i * 64;
  3293. int index(0);
  3294. StringCchPrintfW(pb[index], 64, L"%d %s", itemCache.Size,
  3295. WASABI_API_LNGSTRINGW(itemCache.Size == 1 ? IDS_ITEM : IDS_ITEMS));
  3296. index++;
  3297. if (itemCache.Size)
  3298. {
  3299. int uncert = 0; //bg_total_len_s>>31;
  3300. if (total_length_s < 60*60) StringCchPrintfW(pb[index], 64, L"[%s%u:%02u]", uncert ? L"~" : L"", total_length_s / 60, total_length_s % 60);
  3301. else if (total_length_s < 60*60*24) StringCchPrintfW(pb[index], 64, L"[%s%u:%02u:%02u]", uncert ? L"~" : L"", total_length_s / 60 / 60, (total_length_s / 60) % 60, total_length_s % 60);
  3302. else
  3303. {
  3304. wchar_t days[16] = {0};
  3305. int total_days = total_length_s / (60 * 60 * 24);
  3306. total_length_s -= total_days * 60 * 60 * 24;
  3307. StringCchPrintfW(pb[index], 64,
  3308. WASABI_API_LNGSTRINGW(IDS_LENGTH_DURATION_STRING),
  3309. uncert ? L"~" : L"", total_days,
  3310. WASABI_API_LNGSTRINGW_BUF(total_days == 1 ? IDS_DAY : IDS_DAYS, days, 16),
  3311. total_length_s / 60 / 60, (total_length_s / 60) % 60, total_length_s % 60);
  3312. }
  3313. index++;
  3314. }
  3315. if (total_len_bytes)
  3316. {
  3317. StringCchCopyW(pb[index], 64, L"[");
  3318. WASABI_API_LNG->FormattedSizeString(pb[index] + 1, 64, total_len_bytes);
  3319. StringCchCatW(pb[index], 64, L"]");
  3320. index++;
  3321. }
  3322. unsigned int ms = (UINT)(querytime.QuadPart * 1000 / freq.QuadPart);
  3323. StringCchPrintfW(pb[index], 64, WASABI_API_LNGSTRINGW(IDS_IN_X_SEC), ms / 1000.0f);
  3324. index++;
  3325. SetStatusText(GetDlgItem(hwndDlg, IDC_MEDIASTATUS), (LPCWSTR*)pb, index);
  3326. if (m_bgupdinfoviewerflag)
  3327. {
  3328. m_bgupdinfoviewerflag = 0;
  3329. if (itemCache.Size > 0)
  3330. {
  3331. if (predixisExist)
  3332. {
  3333. if (resultlist.GetSelectedCount() == 1 && isMixable(itemCache.Items[0]))
  3334. {
  3335. SetDlgItemText(hwndDlg, IDC_MIXABLE, L"");
  3336. isMixablePresent = true;
  3337. }
  3338. else
  3339. {
  3340. SetDlgItemText(hwndDlg, IDC_MIXABLE, L"");
  3341. }
  3342. }
  3343. SendMessageW(GetParent(hwndDlg), WM_SHOWFILEINFO, (WPARAM)FALSE, (LPARAM)itemCache.Items[0].filename);
  3344. }
  3345. }
  3346. }
  3347. break;
  3348. case WM_APP + 1:
  3349. bgQuery((resultsniff_funcW)wParam, (int)lParam);
  3350. break;
  3351. case WM_APP + 5:
  3352. {
  3353. // TODO
  3354. int done = (HIWORD(wParam) == 1);
  3355. int code = (LOWORD(wParam));
  3356. for (int i = 0; i < itemCache.Size; i ++)
  3357. {
  3358. // 0 = no change
  3359. // 1 = add cloud
  3360. // 2 = add local
  3361. // 4 = removed
  3362. if (!lstrcmpiW(itemCache.Items[i].filename, (wchar_t *)lParam))
  3363. {
  3364. if (!done)
  3365. {
  3366. setCloudValue(&itemCache.Items[i], L"5");
  3367. }
  3368. else
  3369. {
  3370. if (code == NErr_Success)
  3371. {
  3372. // uploaded ok
  3373. // TODO if going to another device, will need to alter this
  3374. setCloudValue(&itemCache.Items[i], L"0");
  3375. }
  3376. else
  3377. {
  3378. // re-query state
  3379. setCloudValue(&itemCache.Items[i], L"0");
  3380. }
  3381. }
  3382. InvalidateRect(resultlist.getwnd(), NULL, TRUE);
  3383. break;
  3384. }
  3385. }
  3386. break;
  3387. }
  3388. case WM_APP + 6: // handles the ml_cloud 'first pull' announces so we
  3389. { // can then show the cloud column & update the cache
  3390. initColumnsHeader(resultlist.getwnd());
  3391. bgQuery();//(resultsniff_funcW)wParam, (int)lParam);
  3392. break;
  3393. }
  3394. case WM_APP + 104:
  3395. {
  3396. Dialog_UpdateButtonText(hwndDlg, wParam);
  3397. LayoutWindows(hwndDlg, TRUE);
  3398. return 0;
  3399. }
  3400. case WM_PAINT:
  3401. {
  3402. int tab[] = { IDC_LIST2 | DCW_SUNKENBORDER, IDC_QUICKSEARCH | DCW_SUNKENBORDER};
  3403. dialogSkinner.Draw(hwndDlg, tab, 1 + !!IsWindowVisible(GetDlgItem(hwndDlg, IDC_QUICKSEARCH)));
  3404. }
  3405. return 0;
  3406. case WM_ML_CHILDIPC:
  3407. switch (lParam)
  3408. {
  3409. case ML_CHILDIPC_GO_TO_SEARCHBAR:
  3410. SendDlgItemMessage(hwndDlg, IDC_QUICKSEARCH, EM_SETSEL, 0, -1);
  3411. SetFocus(GetDlgItem(hwndDlg, IDC_QUICKSEARCH));
  3412. break;
  3413. case ML_CHILDIPC_REFRESH_SEARCH:
  3414. PostMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_QUICKSEARCH, EN_CHANGE), (LPARAM)GetDlgItem(hwndDlg, IDC_QUICKSEARCH));
  3415. break;
  3416. }
  3417. break;
  3418. case WM_USER + 0x201:
  3419. offsetX = (short)LOWORD(wParam);
  3420. offsetY = (short)HIWORD(wParam);
  3421. g_rgnUpdate = (HRGN)lParam;
  3422. return TRUE;
  3423. case WM_QUERYFILEINFO: Window_OnQueryFileInfo(hwndDlg); break;
  3424. }
  3425. return FALSE;
  3426. }
  3427. //////////////////////////////// Customize columns dialog
  3428. static signed char edit_columnOrder[MAX_COLUMN_ORDER];
  3429. static INT_PTR CALLBACK custColumns_dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3430. {
  3431. static HWND m_curlistbox_hwnd, m_availlistbox_hwnd;
  3432. switch (uMsg)
  3433. {
  3434. case WM_INITDIALOG:
  3435. memcpy(edit_columnOrder, columnOrder, sizeof(edit_columnOrder));
  3436. m_curlistbox_hwnd = GetDlgItem(hwndDlg, IDC_LIST1);
  3437. m_availlistbox_hwnd = GetDlgItem(hwndDlg, IDC_LIST2);
  3438. if (NULL != WASABI_API_APP)
  3439. {
  3440. if (NULL != m_curlistbox_hwnd)
  3441. WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(m_curlistbox_hwnd, TRUE);
  3442. if (NULL != m_availlistbox_hwnd)
  3443. WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(m_availlistbox_hwnd, TRUE);
  3444. }
  3445. case WM_USER + 32:
  3446. {
  3447. for (int i = 0; edit_columnOrder[i] != -1; i++)
  3448. {
  3449. int c = edit_columnOrder[i];
  3450. headerColumn *cl = &columnList[c];
  3451. int column_id = cl->column_id;
  3452. if (column_id == IDS_CLOUD || column_id == IDS_CLOUD_HIDDEN)
  3453. {
  3454. // if no cloud support at all then we hide everything
  3455. if (!cloud_hinst || cloud_hinst == (HINSTANCE)1) continue;
  3456. column_id = ((!cloud_hinst || cloud_hinst == (HINSTANCE)1 ||
  3457. !SendMessageW(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE)) ? IDS_CLOUD_HIDDEN : IDS_CLOUD);
  3458. }
  3459. int r = SendMessageW(m_curlistbox_hwnd, LB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(column_id));
  3460. SendMessageW(m_curlistbox_hwnd, LB_SETITEMDATA, r, c);
  3461. }
  3462. for (int i = 0; i < sizeof(columnList) / sizeof(headerColumn); i++)
  3463. {
  3464. headerColumn *cl = &columnList[i];
  3465. int j;
  3466. for (j = 0; edit_columnOrder[j] != -1 && edit_columnOrder[j] != i; j++);
  3467. if (edit_columnOrder[j] == -1)
  3468. {
  3469. int column_id = cl->column_id;
  3470. if (column_id == IDS_CLOUD || column_id == IDS_CLOUD_HIDDEN)
  3471. {
  3472. // if no cloud support at all then we hide everything
  3473. if (!cloud_hinst || cloud_hinst == (HINSTANCE)1) continue;
  3474. column_id = ((!cloud_hinst || cloud_hinst == (HINSTANCE)1 ||
  3475. !SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE)) ? IDS_CLOUD_HIDDEN : IDS_CLOUD);
  3476. }
  3477. int r = SendMessageW(m_availlistbox_hwnd, LB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(column_id));
  3478. SendMessageW(m_availlistbox_hwnd, LB_SETITEMDATA, r, i);
  3479. }
  3480. }
  3481. }
  3482. break;
  3483. case WM_COMMAND:
  3484. switch (LOWORD(wParam))
  3485. {
  3486. case IDC_DEFS:
  3487. if (!cloud_hinst || cloud_hinst == (HINSTANCE)1)
  3488. memcpy(edit_columnOrder, defColumnOrder, sizeof(defColumnOrder));
  3489. else
  3490. memcpy(edit_columnOrder, defColumnOrderCloud, sizeof(defColumnOrderCloud));
  3491. SendMessage(m_curlistbox_hwnd, LB_RESETCONTENT, 0, 0);
  3492. SendMessage(m_availlistbox_hwnd, LB_RESETCONTENT, 0, 0);
  3493. SendMessage(hwndDlg, WM_USER + 32, 0, 0);
  3494. break;
  3495. case IDC_LIST2:
  3496. if (HIWORD(wParam) != LBN_DBLCLK)
  3497. {
  3498. if (HIWORD(wParam) == LBN_SELCHANGE)
  3499. {
  3500. int r = SendMessage(m_availlistbox_hwnd, LB_GETSELCOUNT, 0, 0) > 0;
  3501. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON2), r);
  3502. }
  3503. return 0;
  3504. }
  3505. case IDC_BUTTON2:
  3506. //add column
  3507. {
  3508. for (int i = 0;i < SendMessage(m_availlistbox_hwnd, LB_GETCOUNT, 0, 0);i++)
  3509. {
  3510. if (SendMessage(m_availlistbox_hwnd, LB_GETSEL, i, 0))
  3511. {
  3512. int c = SendMessage(m_availlistbox_hwnd, LB_GETITEMDATA, i, 0);
  3513. int j;
  3514. for (j = 0;edit_columnOrder[j] != -1;j++);
  3515. edit_columnOrder[j] = (BYTE)c;
  3516. edit_columnOrder[j + 1] = -1;
  3517. SendMessage(m_availlistbox_hwnd, LB_DELETESTRING, i, 0);
  3518. i--;
  3519. int r = SendMessageW(m_curlistbox_hwnd, LB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(columnList[c].column_id));
  3520. SendMessageW(m_curlistbox_hwnd, LB_SETITEMDATA, r, c);
  3521. }
  3522. }
  3523. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON2), 0);
  3524. }
  3525. break;
  3526. case IDC_LIST1:
  3527. if (HIWORD(wParam) != LBN_DBLCLK)
  3528. {
  3529. if (HIWORD(wParam) == LBN_SELCHANGE)
  3530. {
  3531. int r = SendMessage(m_curlistbox_hwnd, LB_GETSELCOUNT, 0, 0) > 0;
  3532. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON3), r);
  3533. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON4), r);
  3534. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON5), r);
  3535. }
  3536. return 0;
  3537. }
  3538. case IDC_BUTTON3:
  3539. //remove column
  3540. {
  3541. for (int i = 0;i < SendMessage(m_curlistbox_hwnd, LB_GETCOUNT, 0, 0);i++)
  3542. {
  3543. if (SendMessage(m_curlistbox_hwnd, LB_GETSEL, i, 0))
  3544. {
  3545. int c = edit_columnOrder[i];
  3546. for (int j = i;edit_columnOrder[j] != -1;j++)
  3547. {
  3548. edit_columnOrder[j] = edit_columnOrder[j + 1];
  3549. }
  3550. SendMessage(m_curlistbox_hwnd, LB_DELETESTRING, i, 0);
  3551. i--;
  3552. int r = SendMessageW(m_availlistbox_hwnd, LB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(columnList[c].column_id));
  3553. SendMessageW(m_availlistbox_hwnd, LB_SETITEMDATA, r, c);
  3554. }
  3555. }
  3556. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON3), 0);
  3557. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON4), 0);
  3558. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON5), 0);
  3559. }
  3560. break;
  3561. case IDC_BUTTON4:
  3562. //move column up
  3563. {
  3564. for (int i = 0;i < (INT)SendMessage(m_curlistbox_hwnd, LB_GETCOUNT, 0, 0);i++)
  3565. {
  3566. if (i != 0 && (INT)SendMessage(m_curlistbox_hwnd, LB_GETSEL, i, 0))
  3567. {
  3568. BYTE c = edit_columnOrder[i - 1];
  3569. edit_columnOrder[i - 1] = edit_columnOrder[i];
  3570. edit_columnOrder[i] = c;
  3571. SendMessage(m_curlistbox_hwnd, LB_DELETESTRING, i - 1, 0);
  3572. int r = (INT)SendMessageW(m_curlistbox_hwnd, LB_INSERTSTRING, i, (LPARAM)WASABI_API_LNGSTRINGW(columnList[c].column_id));
  3573. SendMessage(m_curlistbox_hwnd, LB_SETITEMDATA, r, c);
  3574. }
  3575. }
  3576. }
  3577. break;
  3578. case IDC_BUTTON5:
  3579. //move column down
  3580. {
  3581. int l = SendMessage(m_curlistbox_hwnd, LB_GETCOUNT, 0, 0);
  3582. for (int i = l - 2;i >= 0;i--)
  3583. {
  3584. if (SendMessage(m_curlistbox_hwnd, LB_GETSEL, i, 0))
  3585. {
  3586. BYTE c = edit_columnOrder[i + 1];
  3587. edit_columnOrder[i + 1] = edit_columnOrder[i];
  3588. edit_columnOrder[i] = c;
  3589. SendMessage(m_curlistbox_hwnd, LB_DELETESTRING, i + 1, 0);
  3590. int r = (INT)SendMessageW(m_curlistbox_hwnd, LB_INSERTSTRING, i, (LPARAM)WASABI_API_LNGSTRINGW(columnList[c].column_id));
  3591. SendMessage(m_curlistbox_hwnd, LB_SETITEMDATA, r, c);
  3592. }
  3593. }
  3594. }
  3595. break;
  3596. case IDOK:
  3597. {
  3598. HWND hwndList = resultlist.getwnd();
  3599. memcpy(columnOrder, edit_columnOrder, sizeof(edit_columnOrder));
  3600. if (hwndList)
  3601. {
  3602. initColumnsHeader(hwndList);
  3603. InvalidateRect(hwndList, NULL, TRUE);
  3604. UpdateWindow(hwndList);
  3605. }
  3606. }
  3607. case IDCANCEL:
  3608. EndDialog(hwndDlg, 0);
  3609. break;
  3610. }
  3611. break;
  3612. case WM_DESTROY:
  3613. if (NULL != WASABI_API_APP)
  3614. {
  3615. if (NULL != m_curlistbox_hwnd)
  3616. WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(m_curlistbox_hwnd, FALSE);
  3617. if (NULL != m_availlistbox_hwnd)
  3618. WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(m_availlistbox_hwnd, FALSE);
  3619. WASABI_API_APP->app_removeAccelerators(hwndDlg);
  3620. }
  3621. break;
  3622. }
  3623. return FALSE;
  3624. }
  3625. void customizeColumnsDialog(HWND hwndParent)
  3626. {
  3627. WASABI_API_DIALOGBOXW(IDD_CUSTCOLUMNS, hwndParent, custColumns_dialogProc);
  3628. EatKeyboard();
  3629. }
  3630. bool isMixable(itemRecordW &song)
  3631. {
  3632. if (!song.filename) return false;
  3633. AutoChar charFn(song.filename);
  3634. pluginMessage p = {ML_MSG_PDXS_STATUS, (INT_PTR)(char *)charFn, 0, 0};
  3635. char *text = (char *)SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM) & p, ML_IPC_SEND_PLUGIN_MESSAGE);
  3636. // Analyzed/Identified = mixable
  3637. return text && (text[0] == 'A' || text[0] == 'I');
  3638. }
  3639. void AccessingGracenoteHack(int p)
  3640. {
  3641. if (p == 0)
  3642. {
  3643. GetDlgItemTextW(m_hwnd, IDC_MEDIASTATUS, oldText, 4096);
  3644. SetDlgItemTextW(m_hwnd, IDC_MEDIASTATUS, L"Accessing Gracenote Database");
  3645. }
  3646. else
  3647. {
  3648. SetDlgItemTextW(m_hwnd, IDC_MEDIASTATUS, oldText);
  3649. }
  3650. }