wojack revidoval tento gist 22 hours ago. Přejít na revizi
2 files changed, 3877 insertions
lib-images.js(vytvořil soubor)
| @@ -0,0 +1,1439 @@ | |||
| 1 | + | 'use strict'; | |
| 2 | + | ||
| 3 | + | class LibImages { | |
| 4 | + | constructor() { | |
| 5 | + | this.accessed = 0; | |
| 6 | + | this.asyncBypass = 0; | |
| 7 | + | this.blockWidth = 150; | |
| 8 | + | this.cachePath = grSet.customLibraryDir ? $(`${grCfg.customLibraryDir}library-tree-cache\\`, undefined, true) : `${fb.ProfilePath}cache\\library\\library-tree-cache\\`; | |
| 9 | + | this.cellWidth = 200; | |
| 10 | + | this.column = 0; | |
| 11 | + | this.columnWidth = 150; | |
| 12 | + | this.database = this.newDatabase(); | |
| 13 | + | this.end = 1; | |
| 14 | + | this.groupField = ''; | |
| 15 | + | this.items = []; | |
| 16 | + | this.overlayHeight = 0; | |
| 17 | + | this.panel = {}; | |
| 18 | + | this.preLoadItems = []; | |
| 19 | + | this.rootNo = 4; | |
| 20 | + | this.saveSize = 250; | |
| 21 | + | this.shadow = null; | |
| 22 | + | this.shadowStub = null; | |
| 23 | + | this.start = 0; | |
| 24 | + | this.toSave = []; | |
| 25 | + | this.zooming = false; | |
| 26 | + | ||
| 27 | + | this.bor = { | |
| 28 | + | bot: 6, | |
| 29 | + | cov: 16, | |
| 30 | + | pad: 10, | |
| 31 | + | side: 2 | |
| 32 | + | }; | |
| 33 | + | ||
| 34 | + | this.box = { | |
| 35 | + | h: 100, | |
| 36 | + | w: 100 | |
| 37 | + | }; | |
| 38 | + | ||
| 39 | + | this.cache = {}; | |
| 40 | + | ||
| 41 | + | this.cachesize = { | |
| 42 | + | min: 20 | |
| 43 | + | }; | |
| 44 | + | ||
| 45 | + | this.stub = { | |
| 46 | + | noImg: null, | |
| 47 | + | root: null | |
| 48 | + | }; | |
| 49 | + | ||
| 50 | + | this.style = { | |
| 51 | + | image: 0, | |
| 52 | + | rootComposite: libSet.rootNode && libSet.curRootImg == 3, | |
| 53 | + | vertical: !libSet.albumArtFlowMode ? true : lib.ui.h - lib.panel.search.h > lib.ui.w - lib.ui.sbar.w, | |
| 54 | + | y: 25 | |
| 55 | + | }; | |
| 56 | + | ||
| 57 | + | this.im = { | |
| 58 | + | offset: 0, | |
| 59 | + | y: 0, | |
| 60 | + | w: 120 | |
| 61 | + | }; | |
| 62 | + | ||
| 63 | + | this.interval = { | |
| 64 | + | cache: 1, | |
| 65 | + | preLoad: 7 | |
| 66 | + | }; | |
| 67 | + | ||
| 68 | + | this.labels = { statistics: libSet.itemShowStatistics ? 1 : 0 } | |
| 69 | + | ||
| 70 | + | this.letter = { | |
| 71 | + | albumArtYearAuto: libSet.albumArtYearAuto, | |
| 72 | + | no: 1, | |
| 73 | + | show: libSet.albumArtLetter, | |
| 74 | + | w: 0 | |
| 75 | + | }; | |
| 76 | + | ||
| 77 | + | this.mask = { | |
| 78 | + | fade: null, | |
| 79 | + | circular: null | |
| 80 | + | }; | |
| 81 | + | ||
| 82 | + | this.row = { | |
| 83 | + | h: 80 | |
| 84 | + | }; | |
| 85 | + | ||
| 86 | + | this.text = { | |
| 87 | + | x: 0, | |
| 88 | + | y1: 0, | |
| 89 | + | y2: 0, | |
| 90 | + | h: 20, | |
| 91 | + | w: 20 | |
| 92 | + | }; | |
| 93 | + | ||
| 94 | + | this.timer = { | |
| 95 | + | load: null, | |
| 96 | + | preLoad: null, | |
| 97 | + | save: null | |
| 98 | + | }; | |
| 99 | + | ||
| 100 | + | this.drawDebounce = $Lib.debounce(() => { | |
| 101 | + | lib.panel.treePaint(); | |
| 102 | + | }, 500); | |
| 103 | + | ||
| 104 | + | this.loadThrottle = $Lib.throttle(() => { | |
| 105 | + | if (!lib.panel.imgView) return; | |
| 106 | + | this.getImages(); | |
| 107 | + | }, 40); | |
| 108 | + | ||
| 109 | + | this.rootDebounce = $Lib.debounce(() => { | |
| 110 | + | this.checkRootImg(); | |
| 111 | + | }, 250, { | |
| 112 | + | leading: true, | |
| 113 | + | trailing: true | |
| 114 | + | }); | |
| 115 | + | ||
| 116 | + | this.sizeDebounce = $Lib.debounce(() => { | |
| 117 | + | if (!lib.panel.imgView) return; | |
| 118 | + | this.clearCache(); | |
| 119 | + | this.metrics(); | |
| 120 | + | if (lib.sbar.scroll > lib.sbar.max_scroll) lib.sbar.checkScroll(lib.sbar.max_scroll); | |
| 121 | + | }, 100); | |
| 122 | + | ||
| 123 | + | this.setRoot(); | |
| 124 | + | this.setNoArtist(); | |
| 125 | + | this.setNoCover(); | |
| 126 | + | } | |
| 127 | + | ||
| 128 | + | // * METHODS * // | |
| 129 | + | ||
| 130 | + | async get_album_art_async(handle, art_id, key, ix) { | |
| 131 | + | const result = await utils.GetAlbumArtAsyncV2(0, handle, art_id, false); | |
| 132 | + | const o = this.cache[key]; | |
| 133 | + | const saveName = `${libMD5.hashStr(result.path)}.jpg`; | |
| 134 | + | if (o && o.img == 'called') this.cacheIt(result.image, key, ix, saveName); | |
| 135 | + | } | |
| 136 | + | ||
| 137 | + | async load_image_async(key, image_path, ix, rawCache) { | |
| 138 | + | const image = Date.now() - this.asyncBypass > 5000 ? await gdi.LoadImageAsyncV2(0, image_path) : gdi.Image(image_path); | |
| 139 | + | const o = this.cache[key]; | |
| 140 | + | if (o && o.img == 'called') !rawCache ? this.cacheIt(image, key, ix) : this.cacheItPreLoad(image, key, ix); | |
| 141 | + | } | |
| 142 | + | ||
| 143 | + | cacheIt(image, key, ix, saveName) { | |
| 144 | + | try { | |
| 145 | + | if (!image) { | |
| 146 | + | if (this.style.rootComposite && ix < this.rootNo) this.rootDebounce(); | |
| 147 | + | if (this.albumArtDiskCache && !this.database[key]) { | |
| 148 | + | this.toSave.unshift({ | |
| 149 | + | key, | |
| 150 | + | image: null, | |
| 151 | + | folder: this.cacheFolder, | |
| 152 | + | saveName: 'noAlbumArt', | |
| 153 | + | setKeyOnly: true | |
| 154 | + | }); | |
| 155 | + | } | |
| 156 | + | } | |
| 157 | + | if (image) { | |
| 158 | + | if (this.albumArtDiskCache && saveName) { | |
| 159 | + | if (!this.database[key] && $Lib.file(this.cacheFolder + saveName)) { | |
| 160 | + | this.toSave.unshift({ | |
| 161 | + | key, | |
| 162 | + | image: null, | |
| 163 | + | folder: this.cacheFolder, | |
| 164 | + | saveName, | |
| 165 | + | setKeyOnly: true | |
| 166 | + | }); | |
| 167 | + | } | |
| 168 | + | if (!this.database[key] || !$Lib.file(this.cacheFolder + saveName)) { | |
| 169 | + | image = this.format(image, 1, 'default', this.saveSize, this.saveSize, false, 'save'); | |
| 170 | + | this.toSave.unshift({ | |
| 171 | + | key, | |
| 172 | + | image: image.Clone(0, 0, image.Width, image.Height), | |
| 173 | + | folder: this.cacheFolder, | |
| 174 | + | saveName, | |
| 175 | + | setKeyOnly: false | |
| 176 | + | }); | |
| 177 | + | } | |
| 178 | + | } | |
| 179 | + | ||
| 180 | + | this.checkCache(); | |
| 181 | + | this.format(image, libSet.artId, ['default', 'crop', 'circular'][this.style.image], this.im.w, this.im.w, libSet.albumArtLabelType == 3, 'display', ix, key); | |
| 182 | + | if (this.style.rootComposite && ix < this.rootNo) this.rootDebounce(); | |
| 183 | + | } | |
| 184 | + | ||
| 185 | + | if (!this.timer.save && this.toSave.length) { | |
| 186 | + | this.timer.save = setInterval(() => { | |
| 187 | + | const ln = this.toSave.length; | |
| 188 | + | if (ln) { | |
| 189 | + | if (this.toSave[ln - 1].setKeyOnly) { | |
| 190 | + | this.database[this.toSave[ln - 1].key] = this.toSave[ln - 1].saveName; | |
| 191 | + | $Lib.save(`${this.toSave[ln - 1].folder}database.dat`, JSON.stringify(this.database, null, 3), true); | |
| 192 | + | } else { | |
| 193 | + | const saved = this.toSave[ln - 1].image.SaveAs(this.toSave[ln - 1].folder + this.toSave[ln - 1].saveName, 'image/jpeg'); | |
| 194 | + | if (saved) { | |
| 195 | + | this.database[this.toSave[ln - 1].key] = this.toSave[ln - 1].saveName; | |
| 196 | + | $Lib.save(`${this.toSave[ln - 1].folder}database.dat`, JSON.stringify(this.database, null, 3), true); | |
| 197 | + | } | |
| 198 | + | } | |
| 199 | + | this.toSave.pop(); | |
| 200 | + | } | |
| 201 | + | if (!this.toSave.length) { | |
| 202 | + | clearInterval(this.timer.save); | |
| 203 | + | this.timer.save = null; | |
| 204 | + | } | |
| 205 | + | }, 1000); | |
| 206 | + | } | |
| 207 | + | } | |
| 208 | + | catch (e) { | |
| 209 | + | $Lib.trace(`unable to load thumbnail image: ${key}`); | |
| 210 | + | } | |
| 211 | + | this.drawDebounce(); | |
| 212 | + | } | |
| 213 | + | ||
| 214 | + | cacheItPreLoad(image, key, ix) { | |
| 215 | + | try { | |
| 216 | + | if (image) { | |
| 217 | + | this.checkCache(); | |
| 218 | + | this.format(image, libSet.artId, ['default', 'crop', 'circular'][this.style.image], this.im.w, this.im.w, libSet.albumArtLabelType == 3, 'displayPreload', ix, key); | |
| 219 | + | } | |
| 220 | + | if (this.style.rootComposite && ix < this.rootNo) this.rootDebounce(); | |
| 221 | + | } catch (e) { | |
| 222 | + | $Lib.trace(`unable to load thumbnail image: ${key}`); | |
| 223 | + | } | |
| 224 | + | lib.panel.treePaint(); | |
| 225 | + | } | |
| 226 | + | ||
| 227 | + | checkCache() { | |
| 228 | + | if (!this.memoryLimit()) return; | |
| 229 | + | const ln = this.columns * lib.panel.rows * 3; | |
| 230 | + | if (this.toSave.length > ln) this.toSave.length = ln; | |
| 231 | + | this.preLoadItems = []; | |
| 232 | + | clearInterval(this.timer.preLoad); | |
| 233 | + | this.timer.preLoad = null; | |
| 234 | + | this.items = []; | |
| 235 | + | clearInterval(this.timer.load); | |
| 236 | + | this.timer.load = null; | |
| 237 | + | let keys = Object.keys(this.cache); | |
| 238 | + | const cacheLength = keys.length; | |
| 239 | + | if (lib.pop.tree.length) { | |
| 240 | + | const o = this.cache[lib.pop.tree[0].key]; | |
| 241 | + | if (o) o.accessed = Infinity; | |
| 242 | + | } | |
| 243 | + | this.cache = this.sortCache(this.cache, 'accessed'); | |
| 244 | + | keys = Object.keys(this.cache); | |
| 245 | + | const numToRemove = Math.round((cacheLength - this.cachesize.min) / 2); | |
| 246 | + | if (numToRemove > 0) { | |
| 247 | + | for (let i = 0; i < numToRemove; i++) this.trimCache(keys[i]); | |
| 248 | + | } | |
| 249 | + | } | |
| 250 | + | ||
| 251 | + | checkNowPlaying(item) { | |
| 252 | + | if (!libSet.highLightNowplaying) return false; | |
| 253 | + | return !item.root && lib.pop.inRange(lib.pop.nowp, item.item); | |
| 254 | + | } | |
| 255 | + | ||
| 256 | + | checkRootImg() { | |
| 257 | + | const key = lib.pop.tree.length ? lib.pop.tree[0].key : null; | |
| 258 | + | if (!key) return; | |
| 259 | + | let o = this.cache[key]; | |
| 260 | + | const imgsAvailable = Math.min(Math.round((this.panel.h + this.row.h) / this.row.h) * this.columns, lib.pop.tree.length) - 1; | |
| 261 | + | const n = Math.max(Math.min(Math.floor(Math.sqrt(imgsAvailable)), Infinity), 2); // auto set collage size: limited by no imgs available (per screen): reduce by changing infinity | |
| 262 | + | const cells = Math.pow(n, 2); | |
| 263 | + | this.rootNo = n * n + 1; | |
| 264 | + | if (!o) { | |
| 265 | + | this.cache[key] = { | |
| 266 | + | img: 'called', | |
| 267 | + | accessed: ++this.accessed | |
| 268 | + | }; | |
| 269 | + | } | |
| 270 | + | o = this.cache[key]; | |
| 271 | + | o.img = $Lib.gr(this.cellWidth * n, this.cellWidth * n, true, g => this.createCollage(g, this.cellWidth, this.cellWidth, n, n, cells)); | |
| 272 | + | if (o.img) { | |
| 273 | + | if (this.style.image == 2) this.circularMask(o.img, o.img.Width, o.img.Height); | |
| 274 | + | o.img = o.img.Resize(this.im.w, this.im.w, 7); | |
| 275 | + | if (libSet.albumArtLabelType == 3) this.fadeMask(o.img, o.img.Width, o.img.Height); | |
| 276 | + | } | |
| 277 | + | lib.panel.treePaint(); | |
| 278 | + | } | |
| 279 | + | ||
| 280 | + | checkTooltip(gr, item, x, y1, y2, y3, w, tt1, tt2, tt3, font1, font2, font3) { | |
| 281 | + | if (lib.panel.colMarker) { | |
| 282 | + | if (tt1) tt1 = tt1.replace(Regex.LibMarkerColor, ''); | |
| 283 | + | if (tt2) tt2 = tt2.replace(Regex.LibMarkerColor, ''); | |
| 284 | + | } | |
| 285 | + | let text = tt1 || ''; | |
| 286 | + | if (tt2 && (lib.panel.lines == 2 || lib.panel.lines == 1 && this.labels.statistics)) text += `\n${tt2}`; | |
| 287 | + | if (tt3 && this.labels.statistics) text += `\n${tt3}`; | |
| 288 | + | item.tt = { | |
| 289 | + | text, | |
| 290 | + | x, | |
| 291 | + | y1, | |
| 292 | + | y2, | |
| 293 | + | y3, | |
| 294 | + | w, | |
| 295 | + | 1: tt1 ? gr.CalcTextWidth(tt1, font1) > w ? tt1 : false : false, | |
| 296 | + | 2: tt2 ? gr.CalcTextWidth(tt2, font2) > w ? tt2 : false : false, | |
| 297 | + | 3: tt3 ? gr.CalcTextWidth(tt3, font3) > w ? tt3 : false : false | |
| 298 | + | }; | |
| 299 | + | } | |
| 300 | + | ||
| 301 | + | circularMask(image, w, h) { | |
| 302 | + | image.ApplyMask(this.mask.circular.Resize(w, h)); | |
| 303 | + | } | |
| 304 | + | ||
| 305 | + | clearCache() { | |
| 306 | + | this.accessed = 0; | |
| 307 | + | this.cache = {}; | |
| 308 | + | this.cachesize = { | |
| 309 | + | min: 20 | |
| 310 | + | }; | |
| 311 | + | this.items = []; | |
| 312 | + | } | |
| 313 | + | ||
| 314 | + | createCollage(g, cellWidth, cellHeight, rows, columns, cells) { | |
| 315 | + | let x = 0; | |
| 316 | + | let y = 0; | |
| 317 | + | for (let row = 0; row < rows; row++) { | |
| 318 | + | for (let column = 0; column < columns; column++) { | |
| 319 | + | const idx = column + row * columns + 1; | |
| 320 | + | if (idx <= cells) { | |
| 321 | + | let img = lib.pop.tree.length && lib.pop.tree[idx] ? this.getRootImg(lib.pop.tree[idx].key) : null; | |
| 322 | + | if (!img) img = this.stub.noImg; | |
| 323 | + | if (img) { | |
| 324 | + | let cx = 0; | |
| 325 | + | let cy = 0; | |
| 326 | + | let cw = img.Width; | |
| 327 | + | let ch = img.Height; | |
| 328 | + | if (libSet.albumArtLabelType == 3) { | |
| 329 | + | if (this.style.image != 2) { | |
| 330 | + | ch -= this.overlayHeight; | |
| 331 | + | } else { | |
| 332 | + | cx = cw * 0.1; | |
| 333 | + | cy = ch * 0.1; | |
| 334 | + | cw *= 0.8; | |
| 335 | + | ch = (ch - this.overlayHeight) * 0.8; | |
| 336 | + | } | |
| 337 | + | } else if (this.style.image == 2) { | |
| 338 | + | cx = cw * 0.1; | |
| 339 | + | cy = ch * 0.1; | |
| 340 | + | cw *= 0.8; | |
| 341 | + | ch *= 0.8; | |
| 342 | + | } | |
| 343 | + | img = img.Clone(cx, cy, cw, ch); | |
| 344 | + | img = this.format(img, libSet.artId, 'crop', this.cellWidth, this.cellWidth, false, 'root'); | |
| 345 | + | g.DrawImage(img, x, y, img.Width, img.Height, 0, 0, img.Width, img.Height); | |
| 346 | + | } | |
| 347 | + | x += cellWidth; | |
| 348 | + | } | |
| 349 | + | } | |
| 350 | + | x = 0; | |
| 351 | + | y += cellHeight; | |
| 352 | + | } | |
| 353 | + | x = 0; | |
| 354 | + | y = 0; | |
| 355 | + | for (let column = 0; column < columns; column++) { | |
| 356 | + | x += cellWidth; | |
| 357 | + | if (this.style.image != 2) g.DrawLine(x, 0, x, cellWidth * columns, lib.ui.l.w, lib.ui.col.rootBlend); | |
| 358 | + | } | |
| 359 | + | x = 0; | |
| 360 | + | y = 0; | |
| 361 | + | for (let row = 0; row < rows; row++) { | |
| 362 | + | y += cellHeight; | |
| 363 | + | if (this.style.image != 2) g.DrawLine(x, y, cellWidth * columns, y, lib.ui.l.w, lib.ui.col.rootBlend); | |
| 364 | + | } | |
| 365 | + | if (this.style.image != 2) g.DrawRect(0, 0, cellWidth * columns - 1, cellWidth * columns - 1, 1, lib.ui.col.rootBlend); | |
| 366 | + | } | |
| 367 | + | ||
| 368 | + | createImages() { | |
| 369 | + | this.mask.circular = $Lib.gr(500, 500, true, g => { | |
| 370 | + | g.FillSolidRect(0, 0, 500, 500, RGB(255, 255, 255)); | |
| 371 | + | g.SetSmoothingMode(2); | |
| 372 | + | g.FillEllipse(1, 1, 498, 498, RGBA(0, 0, 0, 255)); | |
| 373 | + | g.SetSmoothingMode(0); | |
| 374 | + | }); | |
| 375 | + | this.mask.fade = $Lib.gr(500, 500, true, g => { | |
| 376 | + | g.FillSolidRect(0, 0, 500, 500, RGB(220, 220, 220)); | |
| 377 | + | }); | |
| 378 | + | } | |
| 379 | + | ||
| 380 | + | draw(gr) { | |
| 381 | + | if (!lib.panel.imgView) return; | |
| 382 | + | let box_x; | |
| 383 | + | let box_y; | |
| 384 | + | let iw; | |
| 385 | + | let ih; | |
| 386 | + | this.getItemsToDraw(); | |
| 387 | + | this.column = 0; | |
| 388 | + | for (let i = this.start; i < this.end; i++) { | |
| 389 | + | const row = this.style.vertical ? Math.floor(i / this.columns) : 0; | |
| 390 | + | box_x = this.style.vertical ? Math.floor(lib.ui.x + this.panel.x + this.column * this.columnWidth + this.bor.side) : Math.floor(lib.ui.x + this.panel.x + i * this.columnWidth + this.bor.side - lib.sbar.delta + (libSet.albumArtFlowMode ? SCALE(18) : 0)); | |
| 391 | + | box_y = this.style.vertical ? Math.floor(lib.ui.y + this.panel.y + row * this.row.h - lib.sbar.delta) : lib.ui.y + this.style.y; | |
| 392 | + | if (box_y >= 0 - this.row.h && box_y < this.panel.y + this.panel.h) { | |
| 393 | + | const item = lib.pop.tree[i]; | |
| 394 | + | lib.pop.getItemCount(item); | |
| 395 | + | const grp = item.grp; | |
| 396 | + | const lot = item.lot; | |
| 397 | + | const statistics = this.labels.statistics ? (!item.root && this.labels.counts ? item.count + (item.count && item._statistics ? ' | ' : '') : '') + item._statistics : ''; | |
| 398 | + | const cur_img = !this.zooming ? this.getImg(item.key) : null; | |
| 399 | + | const nowp = this.checkNowPlaying(item); | |
| 400 | + | // const grpCol = this.getGrpCol(item, nowp, lib.pop.highlight.text && i == lib.pop.m.i); | |
| 401 | + | // const lotCol = this.getLotCol(item, nowp, lib.pop.highlight.text && i == lib.pop.m.i); | |
| 402 | + | const coversRightBottom = ['coversLabelsRight', 'coversLabelsBottom'].includes(grSet.libraryDesign) || libSet.albumArtLabelType === 2; | |
| 403 | + | const updatedNowpBg = pl.col.header_nowplaying_bg !== null; // * Wait until nowplaying bg has a new color to prevent flashing | |
| 404 | + | const colNowPlaying = grSet.libraryBgImg ? RGBtoRGBA(lib.ui.col.nowPlayingBg, grSet.libraryBgRowOpacity) : lib.ui.col.nowPlayingBg; | |
| 405 | + | const colRowStripes = grSet.libraryBgImg ? RGBtoRGBA(lib.ui.col.rowStripes, grSet.libraryBgRowOpacity) : lib.ui.col.rowStripes; | |
| 406 | + | ||
| 407 | + | this.drawSelBg(gr, cur_img, box_x, box_y, i, nowp); | |
| 408 | + | ||
| 409 | + | // * Now playing bg for labels overlay mode ( album art ) | |
| 410 | + | if (this.labels.overlay && !item.root && lib.pop.inRange(lib.pop.nowp, item.item) && updatedNowpBg) { | |
| 411 | + | gr.FillSolidRect(box_x, box_y, this.box.w, this.box.h, colNowPlaying); | |
| 412 | + | } | |
| 413 | + | // * Now playing bg selection with now playing deactivated ( album art ) | |
| 414 | + | if (item.sel && libSet.albumArtShow && !coversRightBottom && updatedNowpBg) { | |
| 415 | + | gr.FillSolidRect(box_x, box_y, this.box.w, this.box.h, colNowPlaying); | |
| 416 | + | } | |
| 417 | + | // * Now playing bg selection with now playing deactivated | |
| 418 | + | if (!lib.pop.highlight.nowPlaying && item.sel && coversRightBottom && updatedNowpBg) { | |
| 419 | + | gr.FillSolidRect(lib.ui.x, box_y, lib.sbar.w ? lib.ui.w - SCALE(42) : lib.ui.w, this.box.h, colNowPlaying); | |
| 420 | + | gr.FillSolidRect(lib.ui.x, box_y, lib.ui.sz.sideMarker, this.box.h, lib.ui.col.sideMarker); | |
| 421 | + | } | |
| 422 | + | // * Marker selection with now playing active | |
| 423 | + | if (lib.pop.highlight.nowPlaying && item.sel && grSet.libraryDesign !== 'flowMode') { | |
| 424 | + | gr.DrawRect(lib.ui.x, box_y, lib.sbar.w ? lib.ui.w - SCALE(42) - 1 : lib.ui.w, this.box.h, 1, lib.ui.col.selectionFrame); | |
| 425 | + | gr.FillSolidRect(lib.ui.x, box_y, lib.ui.sz.sideMarker, this.box.h + 1, lib.ui.col.sideMarker); | |
| 426 | + | } | |
| 427 | + | // * Hide DrawRect gaps when all songs are completely selected and mask lines when selecting now playing | |
| 428 | + | if ((['white', 'black', 'cream'].includes(grSet.theme) && !grSet.styleBlackAndWhite2) && (lib.pop.highlight.nowPlaying && item.sel && !item.root && lib.pop.inRange(lib.pop.nowp, item.item) && coversRightBottom) && updatedNowpBg) { | |
| 429 | + | gr.DrawRect(lib.ui.x, box_y, lib.pop.fullLineSelection ? lib.sbar.w ? lib.ui.w - SCALE(42) : lib.ui.w : lib.ui.w + lib.ui.sz.margin + box_x - lib.ui.x - lib.ui.sz.sideMarker, this.box.h, 1, lib.ui.col.nowPlayingBg); | |
| 430 | + | } | |
| 431 | + | ||
| 432 | + | this.im.y = this.labels.overlay ? this.im.offset + box_y + libSet.thumbNailGapCompact / 2 : this.im.offset + box_y; | |
| 433 | + | if (lib.pop.rowStripes && this.labels.right) { | |
| 434 | + | if (i % 2 == 0) gr.FillSolidRect(0, box_y + 1, lib.panel.tree.stripe.w, this.row.h, colRowStripes /*ui.col.bg1*/); | |
| 435 | + | else gr.FillSolidRect(0, box_y, lib.panel.tree.stripe.w, this.row.h, lib.ui.col.bg2); | |
| 436 | + | } | |
| 437 | + | let x1 = 0; | |
| 438 | + | const x2 = Math.round(box_x + (this.bor.cov) / 2); | |
| 439 | + | let y1 = 0; | |
| 440 | + | let y2 = this.im.y + 2 + this.im.w - this.overlayHeight; | |
| 441 | + | if (cur_img) { | |
| 442 | + | iw = cur_img.Width; | |
| 443 | + | ih = cur_img.Height; | |
| 444 | + | x1 = box_x + Math.round((this.box.w - iw) / 2); | |
| 445 | + | y1 = this.im.y + 1 + this.im.w - ih; | |
| 446 | + | const w = iw; | |
| 447 | + | const h = ih; | |
| 448 | + | if (this.style.dropShadow && this.shadow) { | |
| 449 | + | if (this.style.image) { | |
| 450 | + | gr.DrawImage(this.shadow, x1, y1, this.shadow.Width, this.shadow.Height, 0, 0, this.shadow.Width, this.shadow.Height); | |
| 451 | + | } else { | |
| 452 | + | gr.DrawImage(this.shadow, x1, y1, Math.ceil(w * 1.15), Math.ceil(h * 1.15), 0, 0, this.shadow.Width, this.shadow.Height); | |
| 453 | + | } | |
| 454 | + | } else if (this.style.dropGrad) { | |
| 455 | + | if (this.style.image != 2) { | |
| 456 | + | FillGradRect(gr, x1 + w, y1, 4 * $Lib.scale, h, 0, RGBA(0, 0, 0, 56), 0); | |
| 457 | + | FillGradRect(gr, x1, y1 + h, w, 4 * $Lib.scale, 90, RGBA(0, 0, 0, 56), 0); | |
| 458 | + | } else { | |
| 459 | + | gr.SetSmoothingMode(4); | |
| 460 | + | gr.DrawEllipse(x1, y1, iw, ih, 4 * $Lib.scale, RGBA(0, 0, 0, 32)); | |
| 461 | + | gr.SetSmoothingMode(0); | |
| 462 | + | } | |
| 463 | + | } | |
| 464 | + | ||
| 465 | + | gr.DrawImage(cur_img, x1, y1, w, h, grSet.libraryThumbnailBorder === 'border' ? 0 : 1, grSet.libraryThumbnailBorder === 'border' ? 0 : 1, iw, ih); | |
| 466 | + | ||
| 467 | + | if (this.labels.overlayDark) { | |
| 468 | + | if (item.sel || nowp) gr.FillSolidRect(x2, y2, this.im.w, this.overlayHeight, RGBA(150, 150, 150, 150)); | |
| 469 | + | gr.FillSolidRect(x2, y2, this.im.w, this.overlayHeight, this.getSelBgCol(item, nowp)); | |
| 470 | + | } | |
| 471 | + | if (grSet.libraryThumbnailBorder !== 'none' && (!item.sel || !this.labels.overlay || this.style.image != 2)) { | |
| 472 | + | if (this.style.image != 2) gr.DrawRect(x1, y1, iw - 1, ih - 1, 1, lib.ui.col.imgBor); | |
| 473 | + | else { | |
| 474 | + | gr.SetSmoothingMode(2); | |
| 475 | + | gr.DrawEllipse(x1, y1, iw - 1, ih - 1, 1, lib.ui.col.imgBor); | |
| 476 | + | gr.SetSmoothingMode(0); | |
| 477 | + | } | |
| 478 | + | } | |
| 479 | + | gr.SetSmoothingMode(0); | |
| 480 | + | } | |
| 481 | + | else { | |
| 482 | + | iw = this.im.w; | |
| 483 | + | ih = this.im.w; | |
| 484 | + | x1 = box_x + Math.round((this.box.w - iw) / 2); | |
| 485 | + | y1 = this.im.y + 2 + iw - ih; | |
| 486 | + | if (!item.root) { | |
| 487 | + | if (this.style.dropShadowStub && this.shadowStub) { | |
| 488 | + | gr.DrawImage(this.shadowStub, x1, y1, this.shadowStub.Width, this.shadowStub.Height, 0, 0, this.shadowStub.Width, this.shadowStub.Height); | |
| 489 | + | } else if (this.style.dropGradStub) { | |
| 490 | + | if (this.style.image != 2) { | |
| 491 | + | FillGradRect(gr, x1 + iw - 2 * $Lib.scale, y1, 6 * $Lib.scale, ih, 0, RGBA(0, 0, 0, 56), 0); | |
| 492 | + | FillGradRect(gr, x1, y1 + ih - 2 * $Lib.scale, iw, 6 * $Lib.scale, 90, RGBA(0, 0, 0, 56), 0); | |
| 493 | + | } else { | |
| 494 | + | gr.SetSmoothingMode(2); | |
| 495 | + | gr.DrawEllipse(x1, y1, iw, ih, 4 * $Lib.scale, RGBA(0, 0, 0, 32)); | |
| 496 | + | gr.SetSmoothingMode(0); | |
| 497 | + | } | |
| 498 | + | } | |
| 499 | + | this.stub.noImg && gr.DrawImage(this.stub.noImg, x1, y1, iw, ih, 0, 0, iw, ih); | |
| 500 | + | } | |
| 501 | + | else if (!this.style.rootComposite && this.stub.root) gr.DrawImage(this.stub.root, x1, y1, iw, ih, 0, 0, iw, ih); | |
| 502 | + | ||
| 503 | + | if (this.labels.overlay) { | |
| 504 | + | FillGradRect(gr, x1, y2 - 1, iw / 2, lib.ui.l.w, 1, RGBA(0, 0, 0, 0), lib.ui.col.imgBor); | |
| 505 | + | FillGradRect(gr, x1 + iw / 2, y2 - 1, iw / 2, lib.ui.l.w, 1, lib.ui.col.imgBor, RGBA(0, 0, 0, 0)); | |
| 506 | + | } | |
| 507 | + | if (this.labels.overlayDark) { | |
| 508 | + | if (item.sel || nowp) gr.FillSolidRect(x2, y2, this.im.w, this.overlayHeight, RGBA(150, 150, 150, 150)); | |
| 509 | + | gr.FillSolidRect(x2, y2, this.im.w, this.overlayHeight, this.getSelBgCol(item, nowp)); | |
| 510 | + | } | |
| 511 | + | } | |
| 512 | + | this.drawItemOverlay(gr, item, x1, y1, iw, ih); | |
| 513 | + | if (i == lib.pop.m.i) { | |
| 514 | + | if (lib.pop.highlight.row == 3 || lib.pop.highlight.row == 2 && (((this.labels.overlay || this.labels.hide) && this.style.image != 2))) { | |
| 515 | + | if (!libSet.frameImage) this.drawFrame(gr, box_x, box_y, /*ui.col.frameImgSel*/ lib.ui.col.selectionFrame2, !this.labels.overlay && !this.labels.hide ? 'stnd' : 'thick'); | |
| 516 | + | else this.drawImageFrame(gr, x1, y1, iw, ih, /*ui.col.frameImgSel*/ lib.ui.col.selectionFrame2); | |
| 517 | + | } // else if (lib.pop.highlight.row == 1 && !lib.sbar.draw_timer) gr.FillSolidRect(lib.ui.l.w, y1, lib.ui.sz.sideMarker, this.im.w, lib.ui.col.sideMarker); | |
| 518 | + | } | |
| 519 | + | if (item.sel) { | |
| 520 | + | // if (this.labels.overlay && this.style.image != 2) this.drawFrame(gr, box_x, box_y, /* lib.ui.col.frameImgSel */ lib.ui.col.selectionFrame2, 'thick'); | |
| 521 | + | // else if (this.labels.hide && lib.pop.highlight.row == 3 && libSet.frameImage) this.drawImageFrame(gr, x1, y1, iw, ih, /* lib.ui.col.frameImgSel */ lib.ui.col.selectionFrame2); | |
| 522 | + | if (this.labels.hide && lib.pop.highlight.row == 3 && libSet.frameImage) this.drawImageFrame(gr, x1, y1, iw, ih, /* lib.ui.col.frameImgSel */ lib.ui.col.selectionFrame2); | |
| 523 | + | } | |
| 524 | + | if (!this.labels.hide) { | |
| 525 | + | const x = box_x + this.text.x; | |
| 526 | + | let type = 0; | |
| 527 | + | ||
| 528 | + | const txt_c = | |
| 529 | + | lib.pop.highlight.nowPlaying && !item.root && lib.pop.inRange(lib.pop.nowp, item.item) && updatedNowpBg ? lib.ui.col.text_nowp : | |
| 530 | + | item.sel ? lib.ui.col.textSel : | |
| 531 | + | lib.pop.m.i === i ? lib.ui.col.text_h : lib.ui.col.text; | |
| 532 | + | ||
| 533 | + | if (lib.panel.colMarker) type = item.sel ? 2 : lib.pop.highlight.text && i == lib.pop.m.i ? 1 : 0; | |
| 534 | + | if (!this.labels.overlay) { | |
| 535 | + | y1 = this.im.y + this.text.y1; | |
| 536 | + | y2 = this.im.y + this.text.y2; | |
| 537 | + | const y3 = this.im.y + this.text.y3; | |
| 538 | + | if (lib.panel.lines == 2) { | |
| 539 | + | this.checkTooltip(gr, item, x, y1, y2, y3, this.text.w, grp, lot, statistics, lib.ui.font.group, /*ui.font.lot,*/ lib.ui.font.group, lib.ui.font.statistics); | |
| 540 | + | !lib.panel.colMarker ? gr.GdiDrawText(grp, lib.ui.font.group, txt_c /*grpCol*/, x, y1, this.text.w, this.text.h, !this.labels.right && !item.tt[1] ? lib.panel.cc : lib.panel.lc) : lib.pop.cusCol(gr, grp, item, x, y1, this.text.w, this.text.h, type, nowp, lib.ui.font.group, lib.ui.font.groupEllipsisSpace, 'group'); | |
| 541 | + | !lib.panel.colMarker ? gr.GdiDrawText(lot, lib.ui.font.lot, txt_c /*lotCol*/, x, y2, this.text.w, this.text.h, !this.labels.right && !item.tt[2] ? lib.panel.cc : lib.panel.lc) : lib.pop.cusCol(gr, lot, item, x, y2, this.text.w, this.text.h, type, nowp, lib.ui.font.lot, lib.ui.font.lotEllipsisSpace, 'lott'); | |
| 542 | + | if (statistics) gr.GdiDrawText(statistics, lib.ui.font.statistics, txt_c /*lotCol*/, x, y3, this.text.w, this.text.h, !this.labels.right && !item.tt[2] ? lib.panel.cc : lib.panel.lc); | |
| 543 | + | } else { | |
| 544 | + | this.checkTooltip(gr, item, x, y1, statistics ? y2 : -1, -1, this.text.w, grp, statistics, false, lib.ui.font.group, lib.ui.font.statistics); | |
| 545 | + | !lib.panel.colMarker ? gr.GdiDrawText(grp, lib.ui.font.group, txt_c /*grpCol*/, x, y1, this.text.w, this.text.h, !this.labels.right && !item.tt[1] ? lib.panel.cc : lib.panel.lc) : lib.pop.cusCol(gr, grp, item, x, y1, this.text.w, this.text.h, type, nowp, lib.ui.font.group, lib.ui.font.mainEllipsisSpace, 'group'); | |
| 546 | + | if (statistics) gr.GdiDrawText(statistics, lib.ui.font.statistics, txt_c /*lotCol*/, x, y2, this.text.w, this.text.h, !this.labels.right && !item.tt[2] ? lib.panel.cc : lib.panel.lc); | |
| 547 | + | } | |
| 548 | + | } else { | |
| 549 | + | y1 = this.im.y + this.text.y1; | |
| 550 | + | y2 = y1 + this.text.h * (this.labels.statistics ? 0.93 : 0.9); | |
| 551 | + | const y3 = y2 + this.text.h * 0.95; | |
| 552 | + | if (lib.panel.lines == 2) { | |
| 553 | + | this.checkTooltip(gr, item, x, y1, y2, y3, this.text.w, grp, lot, statistics, lib.ui.font.group, /*ui.font.lot,*/ lib.ui.font.group, lib.ui.font.statistics); | |
| 554 | + | !lib.panel.colMarker ? gr.GdiDrawText(grp, lib.ui.font.group, txt_c /*grpCol*/, x, y1, this.text.w, this.text.h, !item.tt[1] ? lib.panel.cc : lib.panel.lc) : lib.pop.cusCol(gr, grp, item, x, y1, this.text.w, this.text.h, type, nowp, lib.ui.font.group, lib.ui.font.groupEllipsisSpace, 'lott'); | |
| 555 | + | !lib.panel.colMarker ? gr.GdiDrawText(lot, lib.ui.font.lot, txt_c /*lotCol*/, x, y2, this.text.w, this.text.h, !item.tt[2] ? lib.panel.cc : lib.panel.lc) : lib.pop.cusCol(gr, lot, item, x, y2, this.text.w, this.text.h, type, nowp, lib.ui.font.lot, lib.ui.font.lotEllipsisSpace, 'group'); | |
| 556 | + | if (statistics) gr.GdiDrawText(statistics, lib.ui.font.statistics, txt_c /*lotCol*/, x, y3, this.text.w, this.text.h, !item.tt[3] ? lib.panel.cc : lib.panel.lc); | |
| 557 | + | } else { | |
| 558 | + | this.checkTooltip(gr, item, x, y1, statistics ? y2 : -1, -1, this.text.w, grp, statistics, false, lib.ui.font.group, /*ui.font.lot,*/ lib.ui.font.group, lib.ui.font.statistics); | |
| 559 | + | !lib.panel.colMarker ? gr.GdiDrawText(grp, lib.ui.font.group, txt_c /*grpCol*/, x, y1, this.text.w, this.text.h, !item.tt[1] ? lib.panel.cc : lib.panel.lc) : lib.pop.cusCol(gr, grp, item, x, y1, this.text.w, this.text.h, type, nowp, lib.ui.font.group, lib.ui.font.groupEllipsisSpace, 'group'); | |
| 560 | + | if (statistics) gr.GdiDrawText(statistics, lib.ui.font.statistics, txt_c /*lotCol*/, x, y2, this.text.w, this.text.h, !item.tt[2] ? lib.panel.cc : lib.panel.lc); | |
| 561 | + | } | |
| 562 | + | } | |
| 563 | + | } | |
| 564 | + | } | |
| 565 | + | if (this.column == this.columns - 1) this.column = 0; | |
| 566 | + | else this.column++; | |
| 567 | + | } | |
| 568 | + | lib.ui.drawTopBarUnderlay(gr); | |
| 569 | + | } | |
| 570 | + | ||
| 571 | + | drawFrame(gr, box_x, box_y, col, weight) { | |
| 572 | + | let x; | |
| 573 | + | let y; | |
| 574 | + | let w; | |
| 575 | + | let h; | |
| 576 | + | let l_w; | |
| 577 | + | switch (weight) { | |
| 578 | + | case 'stnd': | |
| 579 | + | x = !this.labels.right ? box_x + 1 : lib.ui.sz.pad + 1; | |
| 580 | + | y = box_y + (!this.labels.right ? 1 : 1); | |
| 581 | + | w = !this.labels.right ? this.box.w - 2 : lib.panel.tree.sel.w; | |
| 582 | + | h = this.box.h - (!this.labels.right ? 2 : 0); | |
| 583 | + | l_w = 2; | |
| 584 | + | break; | |
| 585 | + | case 'thick': | |
| 586 | + | x = box_x + Math.round((this.box.w - this.im.w) / 2) + 1; | |
| 587 | + | y = this.im.y + 2; | |
| 588 | + | w = this.im.w; | |
| 589 | + | h = this.im.w - 2; | |
| 590 | + | l_w = 4; | |
| 591 | + | break; | |
| 592 | + | } | |
| 593 | + | gr.DrawRect(x, y, w, h, l_w, col); | |
| 594 | + | } | |
| 595 | + | ||
| 596 | + | drawImageFrame(gr, x, y, w, h, col) { | |
| 597 | + | const l_w = 3; | |
| 598 | + | gr.SetSmoothingMode(2); | |
| 599 | + | if (this.style.image != 2) gr.DrawRect(x + 1, y + 1, w - l_w / 2 - 1, h - l_w / 2 - 1, l_w, col); | |
| 600 | + | else gr.DrawEllipse(x, y, w - l_w / 2, h - l_w / 2, l_w, col); | |
| 601 | + | gr.SetSmoothingMode(0); | |
| 602 | + | } | |
| 603 | + | ||
| 604 | + | drawItemOverlay(gr, item, x, y, w) { | |
| 605 | + | if (item.root) return; | |
| 606 | + | switch (libSet.itemOverlayType) { | |
| 607 | + | case 1: { | |
| 608 | + | if (!item.count) break; | |
| 609 | + | let count_w = Math.max(gr.CalcTextWidth(`${item.count} `, lib.ui.font.tracks), 8); | |
| 610 | + | const count_h = Math.max(gr.CalcTextHeight(item.count, lib.ui.font.tracks), 8); | |
| 611 | + | let count_x = x + (this.style.image != 2 ? w - count_w - 3 : (w - count_w - 2) / 2); | |
| 612 | + | const count_y = y + (this.style.image != 2 ? 0 : count_h / 1.67); | |
| 613 | + | let count = item.count; | |
| 614 | + | let count_h2 = count_h; | |
| 615 | + | if (count_w > this.im.w) { | |
| 616 | + | count = item.count.split(' '); | |
| 617 | + | count_h2 = count_h * 2; | |
| 618 | + | count_w = Math.max(gr.CalcTextWidth(count[0], lib.ui.font.tracks), gr.CalcTextWidth(count[1], lib.ui.font.tracks)); | |
| 619 | + | count_x = x + (this.style.image != 2 ? w - count_w - 3 : (w - count_w - 2) / 2); | |
| 620 | + | gr.SetSmoothingMode(2); | |
| 621 | + | gr.FillSolidRect(count_x, count_y, count_w + 2, count_h2, RGBA(0, 0, 0, 115)); | |
| 622 | + | gr.GdiDrawText(count[0], lib.ui.font.tracks, RGB(255, 255, 255), count_x + 1, count_y, count_w, count_h, this.style.image != 2 ? lib.panel.rc : lib.panel.cc); | |
| 623 | + | gr.GdiDrawText(count[1], lib.ui.font.tracks, RGB(255, 255, 255), count_x + 1, count_y + count_h, count_w, count_h, this.style.image != 2 ? lib.panel.rc : lib.panel.cc); | |
| 624 | + | gr.SetSmoothingMode(0); | |
| 625 | + | } else { | |
| 626 | + | gr.SetSmoothingMode(2); | |
| 627 | + | gr.FillSolidRect(count_x, count_y, count_w + 2, count_h2, RGBA(0, 0, 0, 115)); | |
| 628 | + | gr.GdiDrawText(count, lib.ui.font.tracks, RGB(255, 255, 255), count_x + 1, count_y, count_w, count_h, lib.panel.cc); | |
| 629 | + | gr.SetSmoothingMode(0); | |
| 630 | + | } | |
| 631 | + | break; | |
| 632 | + | } | |
| 633 | + | case 2: { | |
| 634 | + | if (!item.year) break; | |
| 635 | + | const year_w = Math.max(gr.CalcTextWidth(`${item.year} `, lib.ui.font.tracks), 8); | |
| 636 | + | const year_h = Math.max(gr.CalcTextHeight(item.year, lib.ui.font.tracks), 8); | |
| 637 | + | const year_x = x + (this.style.image != 2 ? 0 : (w - year_w - 2) / 2); | |
| 638 | + | const year_y = y + (this.style.image != 2 ? 0 : year_h / 1.67); | |
| 639 | + | gr.SetSmoothingMode(2); | |
| 640 | + | gr.FillSolidRect(year_x, year_y, year_w + 2, year_h, RGBA(0, 0, 0, 115)); | |
| 641 | + | gr.GdiDrawText(item.year, lib.ui.font.tracks, RGB(255, 255, 255), year_x + 1, year_y, year_w, year_h, lib.panel.cc); | |
| 642 | + | gr.SetSmoothingMode(0); | |
| 643 | + | break; | |
| 644 | + | } | |
| 645 | + | } | |
| 646 | + | } | |
| 647 | + | ||
| 648 | + | drawSelBg(gr, cur_img, box_x, box_y, i, nowpOrSel) { | |
| 649 | + | if (this.labels.hide && (this.style.image != 2 || lib.pop.highlight.row == 3 && libSet.frameImage)) return; | |
| 650 | + | let x; | |
| 651 | + | let y; | |
| 652 | + | let w; | |
| 653 | + | let h; | |
| 654 | + | switch (true) { | |
| 655 | + | case nowpOrSel: | |
| 656 | + | // col = lib.ui.col.imgBgSel; | |
| 657 | + | switch (this.labels.overlay || this.labels.hide) { | |
| 658 | + | case true: | |
| 659 | + | x = box_x + Math.round((this.box.w - (cur_img ? cur_img.Width + 1 : this.im.w + 2)) / 2); | |
| 660 | + | y = box_y + (cur_img ? libSet.thumbNailGapCompact / 2 + this.im.w - cur_img.Height + 1 : libSet.thumbNailGapCompact / 2 + 2); | |
| 661 | + | w = cur_img ? cur_img.Width : this.im.w; | |
| 662 | + | h = cur_img ? cur_img.Height : this.im.w; | |
| 663 | + | break; | |
| 664 | + | case false: | |
| 665 | + | x = !this.labels.right ? box_x : lib.ui.x; | |
| 666 | + | y = box_y + (!this.labels.right ? 1 : 1); | |
| 667 | + | w = !this.labels.right ? this.box.w : lib.panel.tree.sel.w; | |
| 668 | + | h = this.box.h - 1; | |
| 669 | + | break; | |
| 670 | + | } | |
| 671 | + | break; | |
| 672 | + | case lib.pop.highlight.row == 2 && i == lib.pop.m.i: | |
| 673 | + | // col = lib.ui.col.bg_h; | |
| 674 | + | if ((this.labels.overlay || this.labels.hide) && this.style.image == 2) { | |
| 675 | + | x = box_x + Math.round((this.box.w - (cur_img ? cur_img.Width : this.im.w)) / 2); | |
| 676 | + | y = box_y + (cur_img ? this.im.w - cur_img.Height : 0); | |
| 677 | + | w = cur_img ? cur_img.Width : this.im.w; | |
| 678 | + | h = cur_img ? cur_img.Height : this.im.w; | |
| 679 | + | } else { | |
| 680 | + | x = !this.labels.right ? box_x : lib.ui.sz.pad; | |
| 681 | + | y = box_y + ((this.labels.overlay || this.labels.hide) ? 0 : (!this.labels.right ? 2 : 1)); | |
| 682 | + | w = !this.labels.right ? this.box.w : lib.panel.tree.sel.w; | |
| 683 | + | h = this.box.h + ((this.labels.overlay || this.labels.hide) ? 2 : 0); | |
| 684 | + | } | |
| 685 | + | break; | |
| 686 | + | } | |
| 687 | + | ||
| 688 | + | x = this.labels.overlay ? box_x + Math.round((this.box.w - (cur_img ? cur_img.Width + 1 : this.im.w + 2)) / 2) : !this.labels.right ? box_x : lib.ui.x; | |
| 689 | + | y = this.labels.overlay ? box_y + (cur_img ? libSet.thumbNailGapCompact / 2 + this.im.w - cur_img.Height + 1 : libSet.thumbNailGapCompact / 2 + 2) : box_y + (!this.labels.right ? 1 : 1); | |
| 690 | + | const coversRight = grSet.libraryDesign === 'coversLabelsRight' || libSet.albumArtLabelType === 2; | |
| 691 | + | ||
| 692 | + | const colNowPlaying = grSet.libraryBgImg ? RGBtoRGBA(lib.ui.col.nowPlayingBg, grSet.libraryBgRowOpacity) : lib.ui.col.nowPlayingBg; | |
| 693 | + | gr.FillSolidRect(x, coversRight ? y - 1 : y, w - (lib.sbar.w && coversRight ? SCALE(42) : 0), coversRight ? h + 2 : h, colNowPlaying); | |
| 694 | + | ||
| 695 | + | if ((grSet.theme !== 'white' && grSet.theme !== 'black') && (!libSet.albumArtShow || libSet.albumArtShow && coversRight) || | |
| 696 | + | (grSet.styleBlackAndWhite || grSet.styleBlackAndWhite2) && libSet.albumArtShow && coversRight) { | |
| 697 | + | gr.FillSolidRect(x, y - 1, lib.ui.sz.sideMarker, h + 2, lib.ui.col.sideMarker); | |
| 698 | + | } | |
| 699 | + | } | |
| 700 | + | ||
| 701 | + | fadeMask(image, w, h) { | |
| 702 | + | const mask = $Lib.gr(w, h, true, g => g.DrawImage(this.mask.fade, 0, h - this.overlayHeight, w, this.overlayHeight, 0, 0, this.mask.fade.Width, this.mask.fade.Height)); | |
| 703 | + | image.ApplyMask(mask); | |
| 704 | + | } | |
| 705 | + | ||
| 706 | + | format(image, n, type, w, h, fade, caller, i, key) { | |
| 707 | + | let ix = 0; | |
| 708 | + | let iy = 0; | |
| 709 | + | let iw = image.Width; | |
| 710 | + | let ih = image.Height; | |
| 711 | + | switch (type) { | |
| 712 | + | case 'crop': | |
| 713 | + | case 'circular': { | |
| 714 | + | const s1 = iw / w; | |
| 715 | + | const s2 = ih / h; | |
| 716 | + | const r = s1 / s2; | |
| 717 | + | if (this.needTrim(n, r)) { | |
| 718 | + | if (s1 > s2) { | |
| 719 | + | iw = Math.round(w * s2); | |
| 720 | + | ix = Math.round((image.Width - iw) / 2); | |
| 721 | + | } else { | |
| 722 | + | ih = Math.round(h * s1); | |
| 723 | + | iy = Math.round((image.Height - ih) / 8); | |
| 724 | + | } | |
| 725 | + | image = image.Clone(ix, iy, iw, ih); | |
| 726 | + | } | |
| 727 | + | image = image.Resize(w, h, 7); | |
| 728 | + | if (type == 'circular') this.circularMask(image, image.Width, image.Height); | |
| 729 | + | break; | |
| 730 | + | } | |
| 731 | + | ||
| 732 | + | default: { | |
| 733 | + | const sc = caller != 'save' ? Math.min(h / ih, w / iw) : Math.max(h / ih, w / iw); | |
| 734 | + | const im_w = Math.round(iw * sc); | |
| 735 | + | const im_h = Math.round(ih * sc); | |
| 736 | + | image = image.Resize(im_w, im_h, 7); | |
| 737 | + | break; | |
| 738 | + | } | |
| 739 | + | } | |
| 740 | + | if (fade) this.fadeMask(image, image.Width, image.Height); | |
| 741 | + | if (caller.startsWith('display')) { | |
| 742 | + | this.cache[key] = { | |
| 743 | + | img: image, | |
| 744 | + | accessed: caller == 'display' ? ++this.accessed : 0 | |
| 745 | + | }; | |
| 746 | + | } | |
| 747 | + | else return image; | |
| 748 | + | } | |
| 749 | + | ||
| 750 | + | getCurrentDatabase() { | |
| 751 | + | this.albumArtDiskCache = libSet.albumArtDiskCache; | |
| 752 | + | if (!this.albumArtDiskCache) return; | |
| 753 | + | const cacheFolder = this.cacheFolder; | |
| 754 | + | $Lib.buildPth(this.cachePath); | |
| 755 | + | this.saveSize = this.im.w > 500 ? 750 : this.im.w > 250 ? 500 : 250; | |
| 756 | + | this.interval = { | |
| 757 | + | cache: this.saveSize == 250 ? 1 : this.saveSize == 500 ? 4 : 9, | |
| 758 | + | preLoad: this.saveSize == 250 ? (libSet.albumArtLabelType != 3 ? 7 : 15) : this.saveSize == 500 ? 20 : 45 | |
| 759 | + | } | |
| 760 | + | this.cacheFolder = `${this.cachePath + ['front', 'back', 'disc', 'icon', 'artist'][libSet.artId] + (this.saveSize == 250 ? '' : this.saveSize)}\\`; | |
| 761 | + | $Lib.create(this.cacheFolder); | |
| 762 | + | this.database = $Lib.jsonParse(`${this.cacheFolder}database.dat`, this.newDatabase(), 'file'); | |
| 763 | + | if (this.cacheFolder != cacheFolder) { | |
| 764 | + | this.preLoadItems = []; | |
| 765 | + | clearInterval(this.timer.preLoad); | |
| 766 | + | this.timer.preLoad = null; | |
| 767 | + | this.items = []; | |
| 768 | + | clearInterval(this.timer.load); | |
| 769 | + | this.timer.load = null; | |
| 770 | + | this.toSave = []; | |
| 771 | + | clearInterval(this.timer.save); | |
| 772 | + | this.timer.save = null; | |
| 773 | + | } | |
| 774 | + | } | |
| 775 | + | ||
| 776 | + | getField(handle, name, arr) { | |
| 777 | + | const f = handle.GetFileInfo(); | |
| 778 | + | if (f) { | |
| 779 | + | for (let i = 0; i < f.MetaCount; ++i) { | |
| 780 | + | let fullName = ''; | |
| 781 | + | for (let j = 0; j < f.MetaValueCount(i); ++j) { | |
| 782 | + | fullName += f.MetaValue(i, j) + (j < f.MetaValueCount(i) - 1 ? ', ' : ''); | |
| 783 | + | if (f.MetaValue(i, j) == name || fullName == name) arr.push(f.MetaName(i).toLowerCase()); | |
| 784 | + | } | |
| 785 | + | } | |
| 786 | + | } | |
| 787 | + | } | |
| 788 | + | ||
| 789 | + | getGrpCol(item, nowp, hover) { | |
| 790 | + | return nowp ? lib.ui.col.nowp : hover ? (lib.panel.textDiffHighlight ? lib.ui.col.nowp : lib.ui.col.text_h) : item.sel ? !this.labels.overlayDark ? lib.ui.col.textSel : lib.ui.col.text : !this.labels.overlayDark ? lib.ui.col.text : RGB(240, 240, 240); | |
| 791 | + | } | |
| 792 | + | ||
| 793 | + | getImages() { | |
| 794 | + | const extraRows = this.albumArtDiskCache ? lib.panel.rows * 2 : lib.panel.rows; // will load any extra including those after any preLoad | |
| 795 | + | ||
| 796 | + | if (!lib.panel.imgView) return; | |
| 797 | + | this.items = []; | |
| 798 | + | let begin = this.start == 0 ? libSet.rootNode ? 1 : 0 : this.start; | |
| 799 | + | const end = this.end != 0 ? Math.min(this.end + this.columns * extraRows, lib.pop.tree.length) : this.end; | |
| 800 | + | for (let i = begin; i < end; i++) { | |
| 801 | + | if (!lib.pop.tree[i]) continue; | |
| 802 | + | const key = lib.pop.tree[i].key; | |
| 803 | + | if (key && !this.cache[key]) { | |
| 804 | + | this.items.push({ | |
| 805 | + | ix: i, | |
| 806 | + | handle: lib.pop.tree[i].handle, | |
| 807 | + | key | |
| 808 | + | }); | |
| 809 | + | } | |
| 810 | + | } | |
| 811 | + | ||
| 812 | + | begin = Math.max(libSet.rootNode ? 1 : 0, begin - this.columns * extraRows); | |
| 813 | + | ||
| 814 | + | let i = end; | |
| 815 | + | while (i--) { | |
| 816 | + | if (i < begin) break; | |
| 817 | + | if (!lib.pop.tree[i]) continue; | |
| 818 | + | const key = lib.pop.tree[i].key; | |
| 819 | + | if (key && !this.cache[key]) { | |
| 820 | + | this.items.push({ | |
| 821 | + | ix: i, | |
| 822 | + | handle: lib.pop.tree[i].handle, | |
| 823 | + | key | |
| 824 | + | }); | |
| 825 | + | } | |
| 826 | + | } | |
| 827 | + | if (!this.items.length) return; | |
| 828 | + | ||
| 829 | + | let interval = !lib.sbar.bar.isDragging && !lib.sbar.touch.dn ? 5 : 50; | |
| 830 | + | const allCached = this.albumArtDiskCache ? this.items.every(v => v.key && this.database[v.key]) : false; | |
| 831 | + | if (allCached) interval = this.interval.cache; | |
| 832 | + | ||
| 833 | + | clearInterval(this.timer.load); | |
| 834 | + | this.timer.load = null; | |
| 835 | + | let j = 0; | |
| 836 | + | this.timer.load = setInterval(() => { | |
| 837 | + | if (j < this.items.length) { | |
| 838 | + | const v = this.items[j]; | |
| 839 | + | const key = v.key; | |
| 840 | + | if (!this.cache[key]) { | |
| 841 | + | if (this.albumArtDiskCache && $Lib.file(this.cacheFolder + this.database[key])) { | |
| 842 | + | this.cache[key] = { | |
| 843 | + | img: 'called', | |
| 844 | + | accessed: ++this.accessed | |
| 845 | + | }; | |
| 846 | + | this.load_image_async(key, this.cacheFolder + this.database[key], v.ix); | |
| 847 | + | } else { | |
| 848 | + | this.cache[key] = { | |
| 849 | + | img: 'called', | |
| 850 | + | accessed: ++this.accessed | |
| 851 | + | }; | |
| 852 | + | if (v.handle) this.get_album_art_async(v.handle, libSet.artId, key, v.ix); | |
| 853 | + | } | |
| 854 | + | } | |
| 855 | + | j++; | |
| 856 | + | } else { | |
| 857 | + | clearInterval(this.timer.load); | |
| 858 | + | this.timer.load = null; | |
| 859 | + | } | |
| 860 | + | }, interval); | |
| 861 | + | } | |
| 862 | + | ||
| 863 | + | getImg(key) { | |
| 864 | + | const o = this.cache[key]; | |
| 865 | + | if (!o || o.img == 'called') return undefined; | |
| 866 | + | o.accessed = ++this.accessed; | |
| 867 | + | return o.img; | |
| 868 | + | } | |
| 869 | + | ||
| 870 | + | getItem(i) { | |
| 871 | + | if (!lib.pop.tree[i]) { | |
| 872 | + | return null; | |
| 873 | + | } | |
| 874 | + | const key = lib.pop.tree[i].key; | |
| 875 | + | if (!this.cache[key] && this.database[key] && this.database[key] != 'noAlbumArt') { | |
| 876 | + | if ($Lib.file(this.cacheFolder + this.database[key])) { // cacheItPreload if file exists | |
| 877 | + | return { | |
| 878 | + | ix: i, | |
| 879 | + | key | |
| 880 | + | } | |
| 881 | + | } | |
| 882 | + | } | |
| 883 | + | return null; | |
| 884 | + | } | |
| 885 | + | ||
| 886 | + | getItemsToDraw(preLoad) { | |
| 887 | + | switch (true) { | |
| 888 | + | case this.style.vertical: | |
| 889 | + | if (lib.pop.tree.length <= lib.panel.rows * this.columns) { | |
| 890 | + | this.start = 0; | |
| 891 | + | this.end = lib.pop.tree.length; | |
| 892 | + | } else { | |
| 893 | + | this.start = Math.round(lib.sbar.delta / this.row.h) * this.columns; | |
| 894 | + | this.start = $Lib.clamp(this.start, 0, this.start - this.columns); | |
| 895 | + | this.end = Math.ceil((lib.sbar.delta + this.panel.h) / this.row.h) * this.columns; | |
| 896 | + | this.end = Math.min(this.end, lib.pop.tree.length); | |
| 897 | + | } | |
| 898 | + | break; | |
| 899 | + | case !this.style.vertical: | |
| 900 | + | if (lib.pop.tree.length <= lib.panel.rows) { | |
| 901 | + | this.start = 0; | |
| 902 | + | this.end = lib.pop.tree.length; | |
| 903 | + | } else { | |
| 904 | + | this.start = Math.round(lib.sbar.delta / this.blockWidth); | |
| 905 | + | this.end = Math.min(this.start + lib.panel.rows + 2, lib.pop.tree.length); | |
| 906 | + | this.start = $Lib.clamp(this.start, 0, this.start - 1); | |
| 907 | + | } | |
| 908 | + | break; | |
| 909 | + | } | |
| 910 | + | this.albumArtDiskCache ? (preLoad ? this.preLoad() : this.getImages()) : this.loadThrottle(); | |
| 911 | + | } | |
| 912 | + | ||
| 913 | + | getLotCol(item, nowp, hover) { | |
| 914 | + | return nowp ? lib.ui.col.nowp : hover ? (lib.panel.textDiffHighlight ? lib.ui.col.nowp : lib.ui.col.text_h) : item.sel ? !this.labels.overlayDark ? lib.ui.col.selBlend : lib.ui.col.lotBlend : !this.labels.overlayDark ? lib.ui.col.lotBlend : RGB(220, 220, 220); | |
| 915 | + | } | |
| 916 | + | ||
| 917 | + | getMostFrequentField(arr) { | |
| 918 | + | const counts = arr.reduce((a, c) => { | |
| 919 | + | a[c] = (a[c] || 0) + 1; | |
| 920 | + | return a; | |
| 921 | + | }, {}); | |
| 922 | + | const maxCount = Math.max(...Object.values(counts)); | |
| 923 | + | const mostFrequent = Object.keys(counts).filter(k => counts[k] === maxCount); | |
| 924 | + | return lib.panel.grp[libSet.viewBy].type.includes(mostFrequent[0]) ? mostFrequent[0] : ''; | |
| 925 | + | } | |
| 926 | + | ||
| 927 | + | getShadow() { | |
| 928 | + | const xy = this.im.w * 0.02; | |
| 929 | + | let wh = this.style.image ? this.im.w * 0.985 : this.im.w; // draw at actual size if possible as faster; regular have to be resized during draw | |
| 930 | + | const sz = this.im.w * 1.17; | |
| 931 | + | if (this.style.image != 2) { | |
| 932 | + | this.shadow = $Lib.gr(sz, sz, true, g => g.FillSolidRect(xy, xy, wh, wh, RGBA(0, 0, 0, 128))); | |
| 933 | + | this.shadow.StackBlur(5); | |
| 934 | + | } else { | |
| 935 | + | this.shadow = $Lib.gr(sz, sz, true, g => g.FillEllipse(xy, xy, wh, wh, RGBA(0, 0, 0, 128))); | |
| 936 | + | this.shadow.StackBlur(4); | |
| 937 | + | } | |
| 938 | + | wh = this.im.w * 0.985; // always drawn at actual size | |
| 939 | + | if (libSet.artId == 4) { | |
| 940 | + | if (libSet.curNoArtistImg == 0 || libSet.curNoArtistImg == 2 || this.style.image == 2) { | |
| 941 | + | this.shadowStub = $Lib.gr(sz, sz, true, g => g.FillEllipse(xy, xy, wh, wh, RGBA(0, 0, 0, 128))); | |
| 942 | + | this.shadowStub.StackBlur(4); | |
| 943 | + | } else if (libSet.curNoArtistImg != 4) { | |
| 944 | + | this.shadowStub = $Lib.gr(sz, sz, true, g => g.FillSolidRect(xy, xy, wh, wh, RGBA(0, 0, 0, 128))); | |
| 945 | + | this.shadowStub.StackBlur(5); | |
| 946 | + | } else { | |
| 947 | + | this.shadowStub = null; | |
| 948 | + | } | |
| 949 | + | } else if (libSet.curNoCoverImg > 2) { | |
| 950 | + | this.shadowStub = $Lib.gr(sz, sz, true, g => g.FillSolidRect(xy, xy, wh, wh, RGBA(0, 0, 0, 128))); | |
| 951 | + | this.shadowStub.StackBlur(5); | |
| 952 | + | } else { | |
| 953 | + | this.shadowStub = null; | |
| 954 | + | } | |
| 955 | + | } | |
| 956 | + | ||
| 957 | + | getRootImg(key) { | |
| 958 | + | const o = this.cache[key]; | |
| 959 | + | if (!o || o.img == 'called') return undefined; | |
| 960 | + | o.accessed = ++this.accessed; | |
| 961 | + | return o.img; | |
| 962 | + | } | |
| 963 | + | ||
| 964 | + | getSelBgCol(item, nowp) { | |
| 965 | + | return nowp || item.sel ? this.albumArtShowLabels ? lib.ui.col.imgBgSel : lib.ui.col.imgOverlaySel : RGBA(0, 0, 0, 175); | |
| 966 | + | } | |
| 967 | + | ||
| 968 | + | getStyle() { | |
| 969 | + | switch (libSet.artId) { | |
| 970 | + | case 0: | |
| 971 | + | return libSet.imgStyleFront; | |
| 972 | + | case 1: | |
| 973 | + | return libSet.imgStyleBack; | |
| 974 | + | case 2: | |
| 975 | + | return libSet.imgStyleDisc; | |
| 976 | + | case 3: | |
| 977 | + | return libSet.imgStyleIcon; | |
| 978 | + | case 4: | |
| 979 | + | return libSet.imgStyleArtist; | |
| 980 | + | } | |
| 981 | + | } | |
| 982 | + | ||
| 983 | + | load() { | |
| 984 | + | const albumArtGrpNames = $Lib.jsonParse(libSet.albumArtGrpNames, {}); | |
| 985 | + | const fields = []; | |
| 986 | + | const mod = lib.pop.tree.length < 1000 ? 1 : lib.pop.tree.length < 3500 ? Math.round(lib.pop.tree.length / 1000) : 3; | |
| 987 | + | const tf_d = FbTitleFormat('[$year(%date%)]'); | |
| 988 | + | this.groupField = albumArtGrpNames[`${lib.panel.grp[libSet.viewBy].type.trim()}${lib.panel.lines}`]; | |
| 989 | + | ||
| 990 | + | lib.pop.tree.forEach((v, i) => { | |
| 991 | + | const item = v.item[0].start; | |
| 992 | + | if (item >= lib.panel.list.Count) return; | |
| 993 | + | const handle = lib.panel.list[item]; | |
| 994 | + | v.handle = handle; | |
| 995 | + | const arr = lib.pop.tree[i].name.split('^@^'); | |
| 996 | + | v.grp = lib.panel.lines == 1 || !libSet.albumArtFlipLabels ? arr[0] : arr[1]; | |
| 997 | + | v.lot = lib.panel.lines == 2 ? !libSet.albumArtFlipLabels ? arr[1] : arr[0] : ''; | |
| 998 | + | v.key = libMD5.hashStr(handle.Path + handle.SubSong + (lib.panel.lines == 1 ? (arr[0] || 'Unknown') : (`${arr[0] || 'Unknown'} - ${arr[1] || 'Unknown'}`)) + libSet.artId); | |
| 999 | + | if (libSet.itemOverlayType == 2) v.year = tf_d.EvalWithMetadb(handle).replace('0000', ''); | |
| 1000 | + | if (!this.groupField && !lib.panel.folderView && i % mod === 0) this.getField(handle, lib.panel.lines == 1 || libSet.albumArtFlipLabels ? v.grp : v.lot, fields); | |
| 1001 | + | }); | |
| 1002 | + | if (!this.groupField && !lib.panel.folderView) { | |
| 1003 | + | this.groupField = this.getMostFrequentField(fields) || '项'; | |
| 1004 | + | this.groupField = $Lib.titlecase(this.groupField); | |
| 1005 | + | } | |
| 1006 | + | ||
| 1007 | + | if (libSet.rootNode) { | |
| 1008 | + | if (!lib.pop.tree[0]) return; | |
| 1009 | + | if (!this.groupField) this.groupField = '项'; | |
| 1010 | + | const plurals = this.groupField.split(' ').map(v => pluralize(v)); | |
| 1011 | + | const pluralField = plurals.join(' ').replace(Regex.LibTypesPlural, '$1 ').replace(Regex.LibSimilarArtist, '$1s ').replace(Regex.LibYearsAlbums, '年份 - 专辑'); | |
| 1012 | + | lib.pop.tree[0].key = lib.pop.tree[0].name; | |
| 1013 | + | const ln1 = lib.pop.tree.length - 1; | |
| 1014 | + | const ln2 = lib.panel.list.Count; | |
| 1015 | + | const nm = `${!libSet.showSource ? '全部' : lib.panel.sourceName} (${ln1}${ln1 > 1 ? ` ${pluralField}` : ` ${this.groupField}`})`; | |
| 1016 | + | if (libSet.rootNode == 3) lib.pop.tree[0].grp = nm; | |
| 1017 | + | else if (lib.panel.lines == 1) lib.pop.tree[0].grp = lib.panel.rootName + (libSet.nodeCounts ? ` (${libSet.nodeCounts == 2 && libSet.rootNode != 3 ? ln1 + (ln1 > 1 ? ` ${pluralField}` : ` ${this.groupField}`) : ln2 + (ln2 > 1 ? ' 个音轨' : ' 个音轨')})` : ''); | |
| 1018 | + | if (lib.panel.lines == 2) { | |
| 1019 | + | if (libSet.rootNode != 3) lib.pop.tree[0].grp = lib.panel.rootName; | |
| 1020 | + | lib.pop.tree[0].lot = libSet.nodeCounts == 2 && libSet.rootNode != 3 ? ln1 + (ln1 > 1 ? ` ${pluralField}` : ` ${this.groupField}`) : ln2 + (ln2 > 1 ? ' 个音轨' : ' 个音轨'); | |
| 1021 | + | } | |
| 1022 | + | } | |
| 1023 | + | this.metrics(); | |
| 1024 | + | lib.panel.treePaint(); | |
| 1025 | + | } | |
| 1026 | + | ||
| 1027 | + | memoryLimit() { | |
| 1028 | + | if (!window.JsMemoryStats) return; | |
| 1029 | + | const limit = !libSet.memoryLimit ? window.JsMemoryStats.TotalMemoryLimit * 0.5 : Math.min(libSet.memoryLimit * 1048576, window.JsMemoryStats.TotalMemoryLimit * 0.8); | |
| 1030 | + | return window.JsMemoryStats.TotalMemoryUsage > limit; | |
| 1031 | + | } | |
| 1032 | + | ||
| 1033 | + | metrics() { | |
| 1034 | + | if (!lib.ui.w || !lib.ui.h) return; | |
| 1035 | + | $Lib.gr(1, 1, false, g => { | |
| 1036 | + | const lineSpacing = this.labels.hide || this.labels.overlay ? Math.max(libSet.verticalAlbumArtPad - 2, 0) : libSet.verticalAlbumArtPad; | |
| 1037 | + | this.letter.w = Math.round(g.CalcTextWidth('W', lib.ui.font.main)); | |
| 1038 | + | this.text.h = Math.max(Math.round(g.CalcTextHeight('String', lib.ui.font.group)) + lineSpacing, Math.round(g.CalcTextHeight('String', lib.ui.font.lot)) + lineSpacing, 10); | |
| 1039 | + | }); | |
| 1040 | + | this.style = { | |
| 1041 | + | dropShadow: libSet.albumArtDropShadow && libSet.albumArtLabelType != 3, | |
| 1042 | + | dropShadowStub: libSet.albumArtDropShadow && libSet.albumArtLabelType != 3 && (libSet.artId == 4 || libSet.curNoCoverImg > 2), | |
| 1043 | + | image: this.getStyle(), | |
| 1044 | + | rootComposite: libSet.rootNode && libSet.curRootImg == 3, | |
| 1045 | + | vertical: !libSet.albumArtFlowMode ? true : lib.ui.h - lib.panel.search.h > lib.ui.w - lib.ui.sbar.w | |
| 1046 | + | } | |
| 1047 | + | ||
| 1048 | + | this.style.dropGrad = libSet.albumArtDropShadow && !this.style.dropShadow; | |
| 1049 | + | this.style.dropGradStub = libSet.albumArtDropShadow && !this.style.dropShadowStub; | |
| 1050 | + | ||
| 1051 | + | this.letter.show = libSet.albumArtLetter; | |
| 1052 | + | this.letter.no = libSet.albumArtLetterNo; | |
| 1053 | + | this.letter.albumArtYearAuto = libSet.albumArtYearAuto; | |
| 1054 | + | ||
| 1055 | + | switch (this.style.vertical) { | |
| 1056 | + | case true: { | |
| 1057 | + | this.labels = { | |
| 1058 | + | hide: !libSet.albumArtLabelType, | |
| 1059 | + | bottom: libSet.albumArtLabelType == 1 || libSet.albumArtFlowMode && libSet.albumArtLabelType == 2, | |
| 1060 | + | right: !libSet.albumArtFlowMode ? libSet.albumArtLabelType == 2 : false, | |
| 1061 | + | overlay: libSet.albumArtLabelType == 3 || libSet.albumArtLabelType == 4, | |
| 1062 | + | overlayDark: libSet.albumArtLabelType == 4, | |
| 1063 | + | flip: libSet.albumArtFlipLabels, | |
| 1064 | + | statistics: libSet.itemShowStatistics ? 1 : 0 | |
| 1065 | + | }; | |
| 1066 | + | this.bor.pad = !this.labels.hide && !this.labels.overlay ? (libSet.thumbNailGapStnd == 0 ? Math.round(this.text.h * (!this.labels.right && grSet.libraryThumbnailSize !== 'playlist' ? 1.05 : 0.75)) : libSet.thumbNailGapStnd - Math.round(2 * $Lib.scale)) : libSet.thumbNailGapCompact; | |
| 1067 | + | this.im.offset = Math.round(!this.labels.hide && !this.labels.overlay ? this.bor.pad / 2 : -2); | |
| 1068 | + | ||
| 1069 | + | if (this.labels.hide || this.labels.overlay) { | |
| 1070 | + | this.panel.y = lib.panel.search.h + Math.round(this.bor.pad / 2); | |
| 1071 | + | this.bor.bot = 0; | |
| 1072 | + | this.bor.side = 0; | |
| 1073 | + | this.bor.cov = libSet.thumbNailGapCompact; | |
| 1074 | + | } else { | |
| 1075 | + | this.panel.y = lib.panel.search.h; | |
| 1076 | + | this.bor.cov = Math.round(this.bor.pad / 2); | |
| 1077 | + | this.bor.side = Math.round(2 * $Lib.scale); | |
| 1078 | + | this.bor.bot = this.bor.side * 2; | |
| 1079 | + | } | |
| 1080 | + | ||
| 1081 | + | const margin = libSet.margin; | |
| 1082 | + | this.panel.x = (libSet.sbarShow != 2 ? Math.max(margin, lib.ui.sbar.w) : margin) + lib.ui.l.w - SCALE(3); | |
| 1083 | + | this.panel.w = lib.ui.w - lib.ui.l.w * 2 - (lib.ui.sbar.type == 0 || libSet.sbarShow != 2 ? Math.max(margin, lib.ui.sbar.w) * 2 + SCALE(20) : (margin * 2 + lib.ui.sbar.w) + SCALE(20)); | |
| 1084 | + | this.panel.h = lib.ui.h - this.panel.y; | |
| 1085 | + | ||
| 1086 | + | this.blockWidth = grSet.libraryThumbnailSize === 'playlist' ? pl.thumbnail_size + (this.bor.side * 2 + this.bor.cov * 2) : | |
| 1087 | + | Math.round(lib.ui.row.h * 4 * $Lib.scale * libSet.zoomImg / 100 * | |
| 1088 | + | [// Thumbnail size | |
| 1089 | + | 0.66, // Mini | |
| 1090 | + | 1, // Small | |
| 1091 | + | 1.5, // Regular | |
| 1092 | + | 1.75, // Medium | |
| 1093 | + | 2.5, // Large | |
| 1094 | + | 3, // XL | |
| 1095 | + | 3.5, // XXL | |
| 1096 | + | 5 // MAX | |
| 1097 | + | ][libSet.thumbNailSize]); | |
| 1098 | + | ||
| 1099 | + | this.columns = libSet.albumArtFlowMode || this.labels.right ? 1 : Math.max(Math.floor(this.panel.w / this.blockWidth), 1); | |
| 1100 | + | let gap = this.panel.w - this.columns * this.blockWidth; | |
| 1101 | + | gap = Math.floor(gap / this.columns); | |
| 1102 | + | this.columnWidth = !this.labels.right ? $Lib.clamp(this.blockWidth + (grSet.libraryThumbnailSize === 'playlist' ? 0 : gap), 10, Math.min(this.panel.w, this.panel.h)) : $Lib.clamp(this.blockWidth, 10, Math.min(this.panel.w, this.panel.h)); | |
| 1103 | + | this.overlayHeight = !this.labels.overlay ? 0 : (lib.panel.lines != 2 ? this.text.h * (1.2 + this.labels.statistics) : Math.round(this.text.h * (2.1 + this.labels.statistics))); | |
| 1104 | + | this.im.w = Math.round(Math.max(this.columnWidth - this.bor.side * 2 - this.bor.cov * 2 - (this.labels.hide || this.labels.overlay ? 1 : 0), 10)); | |
| 1105 | + | ||
| 1106 | + | if (this.labels.hide || this.labels.overlay) { | |
| 1107 | + | this.im.w = Math.round(Math.max(this.columnWidth - this.bor.cov, 10)); | |
| 1108 | + | this.row.h = this.im.w + this.bor.cov; | |
| 1109 | + | } else { | |
| 1110 | + | this.im.w = Math.round(Math.max(this.columnWidth - this.bor.cov * 2 - this.bor.side * 2, 10)); | |
| 1111 | + | this.row.h = !this.labels.right ? this.im.w + this.text.h * (lib.panel.lines + this.labels.statistics) + this.bor.cov * 2 + this.bor.side * 2 : this.im.w + this.bor.pad + 2; | |
| 1112 | + | } | |
| 1113 | + | if (this.row.h > this.panel.h) { | |
| 1114 | + | this.im.w -= this.row.h - this.panel.h; | |
| 1115 | + | this.im.w = Math.max(this.im.w, 10); | |
| 1116 | + | this.row.h = this.panel.h; | |
| 1117 | + | } | |
| 1118 | + | this.box.w = this.columnWidth - this.bor.side * 2; | |
| 1119 | + | this.box.h = this.row.h - (!this.labels.right ? this.bor.side * 2 : 0); | |
| 1120 | + | lib.panel.rows = Math.max(Math.floor(this.panel.h / this.row.h)); | |
| 1121 | + | lib.sbar.metrics(lib.sbar.x, lib.sbar.y, lib.sbar.w, lib.sbar.h, lib.panel.rows, this.row.h, this.style.vertical); | |
| 1122 | + | lib.sbar.setRows(Math.ceil(lib.pop.tree.length / this.columns)); | |
| 1123 | + | break; | |
| 1124 | + | } | |
| 1125 | + | case false: { // only H-Flow | |
| 1126 | + | this.labels = { | |
| 1127 | + | hide: !libSet.albumArtLabelType, | |
| 1128 | + | bottom: libSet.albumArtLabelType == 1 || libSet.albumArtLabelType == 2, | |
| 1129 | + | right: false, | |
| 1130 | + | overlay: libSet.albumArtLabelType == 3 || libSet.albumArtLabelType == 4, | |
| 1131 | + | overlayDark: libSet.albumArtLabelType == 4, | |
| 1132 | + | flip: libSet.albumArtFlipLabels, | |
| 1133 | + | statistics: libSet.itemShowStatistics ? 1 : 0 | |
| 1134 | + | }; | |
| 1135 | + | this.bor.pad = !this.labels.hide && !this.labels.overlay ? (libSet.thumbNailGapStnd == 0 ? Math.round(this.text.h * 1.05) : libSet.thumbNailGapStnd - Math.round(2 * $Lib.scale)) : libSet.thumbNailGapCompact; | |
| 1136 | + | this.im.offset = Math.round(!this.labels.hide && !this.labels.overlay ? this.bor.pad / 2 : -2); | |
| 1137 | + | if (this.labels.hide || this.labels.overlay) { | |
| 1138 | + | this.bor.bot = 0; | |
| 1139 | + | this.bor.side = 0; | |
| 1140 | + | this.bor.cov = libSet.thumbNailGapCompact; | |
| 1141 | + | } else { | |
| 1142 | + | this.bor.cov = Math.round(this.bor.pad / 2); | |
| 1143 | + | this.bor.side = Math.round(2 * $Lib.scale); | |
| 1144 | + | this.bor.bot = this.bor.side * 2; | |
| 1145 | + | } | |
| 1146 | + | this.panel.x = 0; | |
| 1147 | + | const spacer = this.letter.show ? (this.labels.bottom ? this.text.h * 0.5 - this.bor.pad / 4 : this.text.h * 0.75) : (this.labels.bottom ? 0 : Math.round(this.bor.pad / 2)); | |
| 1148 | + | this.panel.y = lib.panel.search.h + spacer - SCALE(3); | |
| 1149 | + | this.panel.h = lib.ui.h - this.panel.y - lib.ui.l.w * 3 - spacer - lib.ui.sbar.w - SCALE(34); | |
| 1150 | + | ||
| 1151 | + | this.panel.w = lib.ui.w; | |
| 1152 | + | if (!this.labels.hide && !this.labels.overlay) { | |
| 1153 | + | this.row.h = this.panel.h; | |
| 1154 | + | const extra = this.text.h * (lib.panel.lines + this.labels.statistics) + this.bor.cov * 2 + this.bor.side * 2; | |
| 1155 | + | this.im.w = Math.min(this.panel.h - extra, this.panel.h - extra); | |
| 1156 | + | this.im.w = $Lib.clamp(this.im.w, 10, Math.round(this.panel.w - (this.bor.cov * 2 + this.bor.side * 2))); | |
| 1157 | + | this.blockWidth = this.im.w + this.bor.cov * 2 + this.bor.side * 2; | |
| 1158 | + | this.row.h = this.im.w + extra; | |
| 1159 | + | } else { | |
| 1160 | + | const extra = this.bor.cov; | |
| 1161 | + | this.im.w = Math.min(this.panel.h - extra, this.panel.h - extra); | |
| 1162 | + | this.im.w = $Lib.clamp(this.im.w, 10, Math.round(this.panel.w - this.bor.cov)); | |
| 1163 | + | this.blockWidth = this.im.w + this.bor.cov; | |
| 1164 | + | this.row.h = this.im.w + extra; | |
| 1165 | + | } | |
| 1166 | + | this.columns = Math.max(Math.floor(this.panel.w / this.blockWidth), 1); | |
| 1167 | + | this.overlayHeight = !this.labels.overlay ? 0 : (lib.panel.lines != 2 ? this.text.h * (1.2 + this.labels.statistics) : Math.round(this.text.h * (2.1 + this.labels.statistics))); | |
| 1168 | + | this.box.w = this.blockWidth - this.bor.side * 2; | |
| 1169 | + | this.box.h = this.row.h - this.bor.bot; | |
| 1170 | + | lib.panel.rows = Math.max(Math.floor(this.panel.w / this.blockWidth)); | |
| 1171 | + | this.columnWidth = this.blockWidth; | |
| 1172 | + | ||
| 1173 | + | lib.sbar.metrics(lib.sbar.x, lib.sbar.y, lib.ui.w, lib.ui.sbar.w, lib.panel.rows, this.blockWidth, this.style.vertical); | |
| 1174 | + | lib.sbar.setRows(Math.ceil(lib.pop.tree.length)); | |
| 1175 | + | break; | |
| 1176 | + | } | |
| 1177 | + | } | |
| 1178 | + | ||
| 1179 | + | this.cellWidth = Math.max(200, this.im.w / 2); | |
| 1180 | + | this.labels.counts = libSet.itemOverlayType != 1 && libSet.nodeCounts; | |
| 1181 | + | this.style.y = this.style.vertical ? Math.floor(this.panel.y + (!this.labels.hide && !this.labels.overlay ? libSet.thumbNailGapStnd / 2 : libSet.thumbNailGapCompact / 2)) : this.panel.y; | |
| 1182 | + | if (this.style.dropShadow) this.getShadow(); | |
| 1183 | + | ||
| 1184 | + | if (!this.labels.hide) { | |
| 1185 | + | if (!this.labels.overlay) { | |
| 1186 | + | this.text.x = !this.labels.right ? Math.round((this.box.w - this.im.w) / 2) : Math.max(Math.round((this.box.w - this.im.w) / 2), 5 * $Lib.scale) * 2 + this.im.w; | |
| 1187 | + | this.text.y1 = !this.labels.right ? this.im.w + Math.round(this.bor.cov * 0.5) : Math.round((this.im.w - this.text.h * lib.panel.lines) / 2) - (this.labels.statistics ? this.text.h / 2 : 0); | |
| 1188 | + | this.text.y2 = !this.labels.right ? Math.round(this.text.y1 + this.text.h * 0.95) : this.text.y1 + this.text.h; | |
| 1189 | + | this.text.y3 = !this.labels.right ? Math.round(this.text.y2 + this.text.h * 0.95) : this.text.y2 + this.text.h; | |
| 1190 | + | this.text.w = !this.labels.right ? this.im.w : this.panel.w - this.text.x - 12; | |
| 1191 | + | } else { | |
| 1192 | + | this.text.x = Math.round(10 + (libSet.thumbNailGapCompact - 3) / 2); | |
| 1193 | + | this.text.y1 = Math.round(this.im.w - this.overlayHeight + 2 + (this.overlayHeight - this.text.h * (lib.panel.lines + this.labels.statistics)) / 2); | |
| 1194 | + | this.text.w = this.box.w - 20 - libSet.thumbNailGapCompact - 6; | |
| 1195 | + | libSet.thumbNailGapCompact = 22; | |
| 1196 | + | } | |
| 1197 | + | } | |
| 1198 | + | ||
| 1199 | + | this.cachesize.min = lib.panel.rows * this.columns * 3 + (this.albumArtDiskCache ? lib.panel.rows * 2 : lib.panel.rows) * this.columns * 2; | |
| 1200 | + | this.createImages(); | |
| 1201 | + | this.getCurrentDatabase(); | |
| 1202 | + | if (libSet.albumArtPreLoad && !this.zooming && this.albumArtDiskCache) this.getItemsToDraw(true); | |
| 1203 | + | this.setNoArtist(); | |
| 1204 | + | this.setNoCover(); | |
| 1205 | + | this.setRoot(); | |
| 1206 | + | if (this.style.rootComposite) this.checkRootImg(); | |
| 1207 | + | const stub = libSet.artId != 4 ? this.no_cover_img : this.no_artist_img; | |
| 1208 | + | if (stub) this.stub.noImg = this.format(stub, libSet.artId, ['default', 'default', 'circular'][this.style.image], this.im.w, this.im.w, libSet.albumArtLabelType == 3, 'noImg'); | |
| 1209 | + | if (this.root_img) this.stub.root = this.format(this.root_img, libSet.artId, 'default', this.im.w, this.im.w, libSet.albumArtLabelType == 3, 'root'); | |
| 1210 | + | lib.panel.treePaint(); | |
| 1211 | + | } | |
| 1212 | + | ||
| 1213 | + | needTrim(n, ratio) { | |
| 1214 | + | return n || Math.abs(ratio - 1) >= 0.05; | |
| 1215 | + | } | |
| 1216 | + | ||
| 1217 | + | newDatabase() { | |
| 1218 | + | return { | |
| 1219 | + | '-----------group key------------': '-----------image key------------.jpg' | |
| 1220 | + | }; | |
| 1221 | + | } | |
| 1222 | + | ||
| 1223 | + | on_key_down() { | |
| 1224 | + | this.zooming = lib.vk.k('zoom'); | |
| 1225 | + | if (this.zooming) { | |
| 1226 | + | clearInterval(this.timer.preLoad); | |
| 1227 | + | this.timer.preLoad = null; | |
| 1228 | + | } | |
| 1229 | + | } | |
| 1230 | + | ||
| 1231 | + | on_key_up() { | |
| 1232 | + | if (this.zooming && this.zooming != lib.vk.k('zoom')) { | |
| 1233 | + | this.zooming = false; | |
| 1234 | + | if (libSet.albumArtPreLoad && this.albumArtDiskCache && lib.panel.imgView) this.metrics(); | |
| 1235 | + | lib.panel.treePaint(); | |
| 1236 | + | } | |
| 1237 | + | } | |
| 1238 | + | ||
| 1239 | + | preLoad() { | |
| 1240 | + | if (!lib.panel.imgView) return; | |
| 1241 | + | this.preLoadItems = []; | |
| 1242 | + | const begin = this.start == 0 ? libSet.rootNode ? 1 : 0 : this.start; | |
| 1243 | + | const end = this.end != 0 ? Math.min(this.end + this.columns, lib.pop.tree.length) : this.end; | |
| 1244 | + | for (let i = begin; i < end; i++) { | |
| 1245 | + | const v = this.getItem(i); | |
| 1246 | + | ||
| 1247 | + | if (v) { | |
| 1248 | + | const key = v.key; | |
| 1249 | + | if (!this.cache[key]) { | |
| 1250 | + | this.cache[key] = { | |
| 1251 | + | img: 'called', | |
| 1252 | + | accessed: ++this.accessed | |
| 1253 | + | }; | |
| 1254 | + | this.load_image_async(key, this.cacheFolder + this.database[key], v.ix, true); | |
| 1255 | + | } | |
| 1256 | + | } | |
| 1257 | + | } | |
| 1258 | + | ||
| 1259 | + | const upBegin = this.start == 0 ? libSet.rootNode ? 1 : 0 : this.start - 1; | |
| 1260 | + | const upEnd = libSet.rootNode ? 1 : 0; | |
| 1261 | + | const downBegin = this.end != 0 ? Math.min(this.end + 1 + this.columns, lib.pop.tree.length) : this.end; | |
| 1262 | + | const downEnd = lib.pop.tree.length; | |
| 1263 | + | ||
| 1264 | + | const doPreload = () => { | |
| 1265 | + | clearInterval(this.timer.preLoad); | |
| 1266 | + | this.timer.preLoad = null; | |
| 1267 | + | let i = downBegin; | |
| 1268 | + | let j = upBegin; | |
| 1269 | + | if (i < downEnd || j > upEnd) { | |
| 1270 | + | this.timer.preLoad = setInterval(() => { | |
| 1271 | + | let v = null; | |
| 1272 | + | if (i < downEnd || j > upEnd) { // interleave | |
| 1273 | + | if (i < downEnd) v = this.getItem(i++); | |
| 1274 | + | if (v) { | |
| 1275 | + | const key = v.key; | |
| 1276 | + | if (!this.cache[key]) { | |
| 1277 | + | this.cache[key] = { | |
| 1278 | + | img: 'called', | |
| 1279 | + | accessed: 0 | |
| 1280 | + | }; | |
| 1281 | + | this.load_image_async(key, this.cacheFolder + this.database[key], v.ix, true); | |
| 1282 | + | } | |
| 1283 | + | } | |
| 1284 | + | ||
| 1285 | + | if (j > upEnd) v = this.getItem(j--); | |
| 1286 | + | if (v) { | |
| 1287 | + | const key = v.key; | |
| 1288 | + | if (!this.cache[key]) { | |
| 1289 | + | this.cache[key] = { | |
| 1290 | + | img: 'called', | |
| 1291 | + | accessed: 0 | |
| 1292 | + | }; | |
| 1293 | + | this.load_image_async(key, this.cacheFolder + this.database[key], v.ix, true); | |
| 1294 | + | } | |
| 1295 | + | } | |
| 1296 | + | } else { | |
| 1297 | + | clearInterval(this.timer.preLoad); | |
| 1298 | + | this.timer.preLoad = null; | |
| 1299 | + | } | |
| 1300 | + | }, this.interval.preLoad); | |
| 1301 | + | } | |
| 1302 | + | }; | |
| 1303 | + | ||
| 1304 | + | doPreload(); | |
| 1305 | + | } | |
| 1306 | + | ||
| 1307 | + | refresh(items) { | |
| 1308 | + | let itemsToRemove = []; | |
| 1309 | + | if (items === 'all') { | |
| 1310 | + | if (!libSet.albumArtDiskCache) return; | |
| 1311 | + | const continue_confirmation = (status, confirmed) => { | |
| 1312 | + | if (confirmed) { | |
| 1313 | + | try { | |
| 1314 | + | this.clearCache(); | |
| 1315 | + | const app = new ActiveXObject('Shell.Application'); | |
| 1316 | + | app.NameSpace(10).MoveHere(this.cachePath); // remove all saved images & databases if albumArtDiskCache | |
| 1317 | + | } catch (e) { | |
| 1318 | + | $Lib.trace('无法清空图片缓存:可以在windows资源管理器中清空'); // Wine fix | |
| 1319 | + | } | |
| 1320 | + | } | |
| 1321 | + | }; | |
| 1322 | + | const caption = '刷新全部图片'; | |
| 1323 | + | const prompt = '此操作将重置曲库(library tree)缩略图磁盘缓存\n\n继续?'; | |
| 1324 | + | const wsh = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.confirm(caption, prompt, '是', '否', false, 'center', continue_confirmation) : true; | |
| 1325 | + | if (wsh) continue_confirmation('ok', $Lib.wshPopup(prompt, caption)); | |
| 1326 | + | return; | |
| 1327 | + | } | |
| 1328 | + | ||
| 1329 | + | const allSelected = lib.pop.sel_items.length == fb.GetLibraryItems().Count; | |
| 1330 | + | const base = this.cachePath + ['front', 'back', 'disc', 'icon', 'artist'][libSet.artId]; | |
| 1331 | + | const databases = [`${base}\\database.dat`, `${base}500\\database.dat`, `${base}750\\database.dat`]; | |
| 1332 | + | if (allSelected) { | |
| 1333 | + | this.clearCache(); // full clear of working cache | |
| 1334 | + | if (!libSet.albumArtDiskCache) return; | |
| 1335 | + | this.database = this.newDatabase(); // full clear of databases for current image type | |
| 1336 | + | databases.forEach(v => { | |
| 1337 | + | if ($Lib.file(v)) $Lib.save(v, JSON.stringify(this.newDatabase(), null, 3), true); | |
| 1338 | + | }); | |
| 1339 | + | return; | |
| 1340 | + | } | |
| 1341 | + | ||
| 1342 | + | // refresh selected images | |
| 1343 | + | items.Convert().forEach(v => { | |
| 1344 | + | const item = lib.panel.list.Find(v); | |
| 1345 | + | let ind = -1; | |
| 1346 | + | lib.pop.tree.forEach((v, j) => { | |
| 1347 | + | if (!v.root && lib.pop.inRange(item, v.item)) ind = j; | |
| 1348 | + | }); | |
| 1349 | + | if (ind != -1) itemsToRemove.push(lib.pop.tree[ind].key); | |
| 1350 | + | }); | |
| 1351 | + | itemsToRemove = [...new Set(itemsToRemove)]; | |
| 1352 | + | itemsToRemove.forEach(v => this.trimCache(v)); // clear working cache of selected keys: won't check if same images are used with other keys | |
| 1353 | + | if (!libSet.albumArtDiskCache) return; | |
| 1354 | + | let imgsToRemove = itemsToRemove.map(v => this.database[v]); | |
| 1355 | + | imgsToRemove = [...new Set(imgsToRemove)]; | |
| 1356 | + | databases.forEach(v => { | |
| 1357 | + | if ($Lib.file(v)) { | |
| 1358 | + | const cur_db = v == `${this.cacheFolder}database.dat`; | |
| 1359 | + | const imgDatabase = $Lib.jsonParse(v, this.newDatabase(), 'file'); | |
| 1360 | + | Object.entries(imgDatabase).forEach(w => { // clear working cache & database of all keys that reference a particular image: this should always work even if images in use | |
| 1361 | + | if (w[0] != '-----------group key------------') { | |
| 1362 | + | if (imgsToRemove.includes(w[1]) || !$Lib.file(this.cacheFolder + w[1])) { // images are refreshed as loaded, overwriting existing | |
| 1363 | + | if (cur_db) this.trimCache(w[0]); | |
| 1364 | + | delete imgDatabase[w[0]]; | |
| 1365 | + | } | |
| 1366 | + | } | |
| 1367 | + | }); | |
| 1368 | + | Object.entries(imgDatabase).forEach(w => { | |
| 1369 | + | if (w[0] != '-----------group key------------') { | |
| 1370 | + | if (w[1] != 'noAlbumArt' && !$Lib.file(this.cacheFolder + w[1])) delete imgDatabase[w[0]]; | |
| 1371 | + | } | |
| 1372 | + | }); // remove any user deleted images from database | |
| 1373 | + | $Lib.save(v, JSON.stringify(imgDatabase, null, 3), true); | |
| 1374 | + | } | |
| 1375 | + | }); | |
| 1376 | + | this.getCurrentDatabase(); | |
| 1377 | + | } | |
| 1378 | + | ||
| 1379 | + | setNoArtist() { | |
| 1380 | + | this.artist_images = lib_my_utils.getImageAssets('noArtist').sort(); | |
| 1381 | + | libSet.curNoArtistImg = $Lib.clamp($Lib.value(libSet.curNoArtistImg, 0, 0), 0, this.artist_images.length - 1); | |
| 1382 | + | const artistImages = this.artist_images.map(v => ({ | |
| 1383 | + | name: utils.SplitFilePath(v)[1], | |
| 1384 | + | path: `file://${v.replace('noArtist', 'noArtist/small')}` | |
| 1385 | + | })); | |
| 1386 | + | this.no_artist_img = gdi.Image(this.artist_images[libSet.curNoArtistImg]); | |
| 1387 | + | libSet.noArtistImages = JSON.stringify(artistImages); | |
| 1388 | + | } | |
| 1389 | + | ||
| 1390 | + | setNoCover() { | |
| 1391 | + | this.cover_images = lib_my_utils.getImageAssets('noCover').sort(); | |
| 1392 | + | libSet.curNoCoverImg = $Lib.clamp($Lib.value(libSet.curNoCoverImg, 0, 0), 0, this.cover_images.length - 1); | |
| 1393 | + | const coverImages = this.cover_images.map(v => ({ | |
| 1394 | + | name: utils.SplitFilePath(v)[1], | |
| 1395 | + | path: `file://${v.replace('noCover', 'noCover/small')}` | |
| 1396 | + | })); | |
| 1397 | + | this.no_cover_img = gdi.Image(this.cover_images[libSet.curNoCoverImg]); | |
| 1398 | + | libSet.noCoverImages = JSON.stringify(coverImages); | |
| 1399 | + | } | |
| 1400 | + | ||
| 1401 | + | setRoot() { | |
| 1402 | + | this.root_images = lib_my_utils.getImageAssets('root').sort(); | |
| 1403 | + | libSet.curRootImg = $Lib.clamp($Lib.value(libSet.curRootImg, 0, 0), 0, this.root_images.length - 1); | |
| 1404 | + | const rootImages = this.root_images.map(v => ({ | |
| 1405 | + | name: utils.SplitFilePath(v)[1], | |
| 1406 | + | path: `file://${v.replace('root', 'root/small')}` | |
| 1407 | + | })); | |
| 1408 | + | if (libSet.rootNode && libSet.curRootImg == 3) { | |
| 1409 | + | this.style.rootComposite = true; | |
| 1410 | + | this.root_img = null; | |
| 1411 | + | } else { | |
| 1412 | + | this.style.rootComposite = false; | |
| 1413 | + | this.root_img = gdi.Image(this.root_images[libSet.curRootImg]); | |
| 1414 | + | } | |
| 1415 | + | libSet.rootImages = JSON.stringify(rootImages); | |
| 1416 | + | } | |
| 1417 | + | ||
| 1418 | + | sort(data, prop) { | |
| 1419 | + | data.sort((a, b) => a[prop] - b[prop]); | |
| 1420 | + | return data; | |
| 1421 | + | } | |
| 1422 | + | ||
| 1423 | + | sortCache(o, prop) { | |
| 1424 | + | const sorted = {}; | |
| 1425 | + | Object.keys(o).sort((a, b) => o[a][prop] - o[b][prop]).forEach(key => sorted[key] = o[key]); | |
| 1426 | + | return sorted; | |
| 1427 | + | } | |
| 1428 | + | ||
| 1429 | + | trimCache(key) { | |
| 1430 | + | delete this.cache[key]; | |
| 1431 | + | } | |
| 1432 | + | } | |
| 1433 | + | ||
| 1434 | + | /** | |
| 1435 | + | * The instance of `LibImages` class for library image operations. | |
| 1436 | + | * @typedef {LibImages} | |
| 1437 | + | * @global | |
| 1438 | + | */ | |
| 1439 | + | const libImg = new LibImages(); | |
lib-populate.js(vytvořil soubor)
| @@ -0,0 +1,2438 @@ | |||
| 1 | + | 'use strict'; | |
| 2 | + | ||
| 3 | + | class LibPopulate { | |
| 4 | + | constructor() { | |
| 5 | + | this.alt_dbl_clicked = false; | |
| 6 | + | this.childCount = 0; | |
| 7 | + | this.clicked_on = 'none'; | |
| 8 | + | this.cur = []; | |
| 9 | + | this.cur_ix = 0; | |
| 10 | + | this.customSort = FbTitleFormat(libSet.customSort); | |
| 11 | + | this.dbl_clicked = false; | |
| 12 | + | this.expandedTracks = 0; | |
| 13 | + | this.expandLmt = 500; | |
| 14 | + | this.hotKeys = $Lib.split(libSet.hotKeys, 0); | |
| 15 | + | this.hand = false; | |
| 16 | + | this.id = ''; | |
| 17 | + | this.inlineRoot = libSet.rootNode && (libSet.inlineRoot || libSet.facetView); | |
| 18 | + | this.is_focused = false; | |
| 19 | + | this.last_sel = -1; | |
| 20 | + | this.lbtnDn = false; | |
| 21 | + | this.libItems = false; | |
| 22 | + | this.mbtn_dbl_clicked = false; | |
| 23 | + | this.nd = []; | |
| 24 | + | this.nowp = -1; | |
| 25 | + | this.rows = 0; | |
| 26 | + | this.sel_items = []; | |
| 27 | + | this.selection_holder = fb.AcquireUiSelectionHolder(); | |
| 28 | + | this.selList = null; | |
| 29 | + | this.setFocus = false; | |
| 30 | + | this.specialChar = '[' + [Unicode.Apostrophe, | |
| 31 | + | Unicode.SoftHyphen, Unicode.ArmenianHyphen, Unicode.Hyphen, Unicode.NonBreakingHyphen, | |
| 32 | + | Unicode.FigureDash, Unicode.EnDash, Unicode.EmDash, Unicode.SmallEmDash | |
| 33 | + | ].join('') + ']'; | |
| 34 | + | this.sy_sz = 8; | |
| 35 | + | this.tree = []; | |
| 36 | + | ||
| 37 | + | this.cache = { | |
| 38 | + | standard: {}, | |
| 39 | + | filter: {}, | |
| 40 | + | search: {} | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | this.highlight = {}; | |
| 44 | + | ||
| 45 | + | this.getMainMenuIndex = { | |
| 46 | + | add: parseFloat(this.hotKeys[3]), | |
| 47 | + | collapseAll: parseFloat(this.hotKeys[1]), | |
| 48 | + | insert: parseFloat(this.hotKeys[5]), | |
| 49 | + | new: parseFloat(this.hotKeys[7]), | |
| 50 | + | searchClear: parseFloat(this.hotKeys[11]), | |
| 51 | + | searchFocus: parseFloat(this.hotKeys[9]) | |
| 52 | + | }; | |
| 53 | + | ||
| 54 | + | this.last_pressed_coord = { | |
| 55 | + | x: undefined, | |
| 56 | + | y: undefined | |
| 57 | + | }; | |
| 58 | + | ||
| 59 | + | this.m = { | |
| 60 | + | br: -1, | |
| 61 | + | cur_br: 0, | |
| 62 | + | i: -1 | |
| 63 | + | }; | |
| 64 | + | ||
| 65 | + | this.row = { | |
| 66 | + | cur: 0, | |
| 67 | + | i: -1, | |
| 68 | + | lineMax: [], | |
| 69 | + | note_w: 0 | |
| 70 | + | }; | |
| 71 | + | ||
| 72 | + | this.tf = { | |
| 73 | + | added: FbTitleFormat(libSet.tfAdded), | |
| 74 | + | bitrate: FbTitleFormat('%bitrate%'), | |
| 75 | + | bytes: FbTitleFormat('%path%|%filesize%'), | |
| 76 | + | date: FbTitleFormat(libSet.tfDate), | |
| 77 | + | firstPlayed: FbTitleFormat(libSet.tfFirstPlayed), | |
| 78 | + | lastPlayed: FbTitleFormat(libSet.tfLastPlayed), | |
| 79 | + | pc: FbTitleFormat(libSet.tfPc), | |
| 80 | + | popularity: FbTitleFormat(libSet.tfPopularity), | |
| 81 | + | rating: FbTitleFormat(libSet.tfRating) | |
| 82 | + | }; | |
| 83 | + | ||
| 84 | + | this.triangle = { | |
| 85 | + | expand: null, | |
| 86 | + | highlight: null, | |
| 87 | + | select: null | |
| 88 | + | }; | |
| 89 | + | ||
| 90 | + | this.collator = new Intl.Collator(undefined, { | |
| 91 | + | sensitivity: 'accent', | |
| 92 | + | numeric: true | |
| 93 | + | }); | |
| 94 | + | ||
| 95 | + | this.setActions(); | |
| 96 | + | this.setValues(); | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | // * METHODS * // | |
| 100 | + | ||
| 101 | + | activateTooltip(value) { | |
| 102 | + | if (!grSet.showTooltipLibrary && !grSet.showTooltipTruncated || libTooltip.Text == value) return; | |
| 103 | + | this.checkTooltipFont('tree'); | |
| 104 | + | if (grSet.showStyledTooltips) { | |
| 105 | + | grm.ui.styledTooltipText = value; | |
| 106 | + | } else { | |
| 107 | + | libTooltip.Text = value; | |
| 108 | + | libTooltip.Activate(); | |
| 109 | + | } | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | add(x, y, pl) { | |
| 113 | + | if (y < lib.panel.search.h) return; | |
| 114 | + | const ix = this.get_ix(x, y, true, false); | |
| 115 | + | lib.panel.pos = ix; | |
| 116 | + | if (ix < this.tree.length && ix >= 0) { | |
| 117 | + | if (this.check_ix(this.tree[ix], x, y, true)) { | |
| 118 | + | this.clearSelected(); | |
| 119 | + | this.tree[ix].sel = true; | |
| 120 | + | this.getTreeSel(); | |
| 121 | + | this.load(this.sel_items, true, true, false, pl, false); | |
| 122 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 123 | + | } | |
| 124 | + | } | |
| 125 | + | } | |
| 126 | + | ||
| 127 | + | addItems(arr, item) { | |
| 128 | + | item.forEach(v => { | |
| 129 | + | for (let i = v.start; i <= v.end; i++) arr.push(i); | |
| 130 | + | }); | |
| 131 | + | } | |
| 132 | + | ||
| 133 | + | arrayToRange(array) { | |
| 134 | + | return array.slice().sort((a, b) => a - b).reduce((ranges, value) => { | |
| 135 | + | const lastIndex = ranges.length - 1; | |
| 136 | + | if (lastIndex === -1 || ranges[lastIndex].max !== value - 1) { | |
| 137 | + | ranges.push({ min: value, max: value }); | |
| 138 | + | } else { | |
| 139 | + | ranges[lastIndex].max = value; | |
| 140 | + | } | |
| 141 | + | return ranges; | |
| 142 | + | }, []).map((range) => range.min !== range.max ? `${range.min}-${range.max}` : range.min.toString()); | |
| 143 | + | } | |
| 144 | + | ||
| 145 | + | branch(br, base, node, block) { | |
| 146 | + | if (!br || br.track || !lib.lib.initialised || lib.lib.list.Count != lib.lib.libNode.length) return; | |
| 147 | + | const ix = this.showTracks ? 2 : 3; | |
| 148 | + | const l = base ? 0 : this.rootNode ? br.level : br.level + 1; | |
| 149 | + | if (base) node = false; | |
| 150 | + | let i = 0; | |
| 151 | + | let n = ''; | |
| 152 | + | let n_o = '#get_branch#'; | |
| 153 | + | let nU = ''; | |
| 154 | + | this.range(br.item).forEach(v => { | |
| 155 | + | n = lib.lib.node[v][l]; | |
| 156 | + | nU = n.toUpperCase(); | |
| 157 | + | if (n_o != nU) { | |
| 158 | + | n_o = nU; | |
| 159 | + | if (lib.panel.multiPrefix) n = lib.lib.prefixes(n); | |
| 160 | + | br.child[i] = { | |
| 161 | + | nm: n, | |
| 162 | + | sel: false, | |
| 163 | + | child: [], | |
| 164 | + | track: l > lib.lib.node[v].length - ix, | |
| 165 | + | item: [v], | |
| 166 | + | srt: lib.lib.sort(n) | |
| 167 | + | }; | |
| 168 | + | i++; | |
| 169 | + | } else br.child[i - 1].item.push(v); | |
| 170 | + | }); | |
| 171 | + | this.condense(br.child); | |
| 172 | + | this.buildTree(lib.lib.root, 0, node, true, block); | |
| 173 | + | } | |
| 174 | + | ||
| 175 | + | branchChange(br) { | |
| 176 | + | const arr = br.level == 0 ? lib.lib.root : this.tree[br.par].child; | |
| 177 | + | this.childCount = 0; | |
| 178 | + | this.getChildCount(arr, br.ix); | |
| 179 | + | arr.forEach(v => v.child = []); | |
| 180 | + | return this.childCount; | |
| 181 | + | } | |
| 182 | + | ||
| 183 | + | branchCount(br, base, node, block, key, type) { | |
| 184 | + | if (!br || !lib.lib.node.length) return; | |
| 185 | + | if (this.cache[type][key]) return this.cache[type][key].value; | |
| 186 | + | const l = base ? 0 : this.rootNode ? br.level : br.level + 1; | |
| 187 | + | const b = []; | |
| 188 | + | let n = ''; | |
| 189 | + | let n_o = '#get_branch#'; | |
| 190 | + | let nU = ''; | |
| 191 | + | if (base) node = false; | |
| 192 | + | const full = !!br.root; | |
| 193 | + | this.range(br.item).forEach(v => { | |
| 194 | + | if (l < lib.lib.node[v].length) { | |
| 195 | + | n = lib.lib.node[v][l]; | |
| 196 | + | nU = n.toUpperCase(); | |
| 197 | + | if (n_o != nU) { | |
| 198 | + | n_o = nU; | |
| 199 | + | if (lib.panel.multiPrefix) n = lib.lib.prefixes(n); | |
| 200 | + | b.push({ | |
| 201 | + | nm: n, | |
| 202 | + | srt: lib.lib.sort(n) | |
| 203 | + | }); | |
| 204 | + | } | |
| 205 | + | } | |
| 206 | + | }); | |
| 207 | + | if (!lib.panel.multiProcess && (!node || node && !full)) this.merge(b, true); | |
| 208 | + | if (lib.panel.multiProcess) { | |
| 209 | + | const multi_cond = []; | |
| 210 | + | const multi_obj = []; | |
| 211 | + | const multi_rem = []; | |
| 212 | + | const nm_arr = []; | |
| 213 | + | let h = -1; | |
| 214 | + | let j = 0; | |
| 215 | + | let multi = []; | |
| 216 | + | n = ''; | |
| 217 | + | n_o = '#condense#'; | |
| 218 | + | nU = ''; | |
| 219 | + | b.forEach((v, i) => { | |
| 220 | + | if (v.nm.includes('@@')) { | |
| 221 | + | multi = this.getAllCombinations(v.nm); | |
| 222 | + | multi_rem.push(i); | |
| 223 | + | multi.forEach(w => { | |
| 224 | + | multi_obj.push({ | |
| 225 | + | nm: w.join(''), | |
| 226 | + | srt: lib.lib.sort(w.join('')) | |
| 227 | + | }); | |
| 228 | + | }); | |
| 229 | + | } | |
| 230 | + | }); | |
| 231 | + | let i = multi_rem.length; | |
| 232 | + | while (i--) b.splice(multi_rem[i], 1); | |
| 233 | + | this.sort(multi_obj); | |
| 234 | + | multi_obj.forEach(v => { | |
| 235 | + | n = v.nm; | |
| 236 | + | nU = n.toUpperCase(); | |
| 237 | + | if (n_o != nU) { | |
| 238 | + | n_o = nU; | |
| 239 | + | multi_cond[j] = { | |
| 240 | + | nm: n, | |
| 241 | + | srt: v.srt | |
| 242 | + | }; | |
| 243 | + | j++; | |
| 244 | + | } | |
| 245 | + | }); | |
| 246 | + | b.forEach(v => { | |
| 247 | + | v.nm = v.nm.replace(Regex.LibMarkerMultiProcess, ''); | |
| 248 | + | nm_arr.push(v.nm); | |
| 249 | + | }); | |
| 250 | + | multi_cond.forEach((v, i) => { | |
| 251 | + | h = nm_arr.indexOf(v.nm); | |
| 252 | + | if (h != -1) multi_cond.splice(i, 1); | |
| 253 | + | }); | |
| 254 | + | multi_cond.forEach((v, i) => b.splice(i + 1, 0, { | |
| 255 | + | nm: v.nm, | |
| 256 | + | srt: v.srt | |
| 257 | + | })); | |
| 258 | + | this.merge(b, true); | |
| 259 | + | } | |
| 260 | + | this.cache[type][key] = { | |
| 261 | + | value: b.length, | |
| 262 | + | items: [] | |
| 263 | + | } | |
| 264 | + | return b.length; | |
| 265 | + | } | |
| 266 | + | ||
| 267 | + | buildTree(br, level, node, full, block) { | |
| 268 | + | const l = !this.rootNode ? level : level - 1; | |
| 269 | + | let i = 0; | |
| 270 | + | let j = 0; | |
| 271 | + | if (!br[0].sorted) { | |
| 272 | + | switch (lib.panel.multiProcess) { | |
| 273 | + | case false: | |
| 274 | + | if (!node || node && !full) this.merge(br); | |
| 275 | + | break; | |
| 276 | + | case true: { | |
| 277 | + | const multi_cond = []; | |
| 278 | + | const multi_obj = []; | |
| 279 | + | const multi_rem = []; | |
| 280 | + | const nm_arr = []; | |
| 281 | + | let h = -1; | |
| 282 | + | let multi = []; | |
| 283 | + | let n = ''; | |
| 284 | + | let n_o = '#condense#'; | |
| 285 | + | let nU = ''; | |
| 286 | + | br.forEach((v, i) => { | |
| 287 | + | if (v.nm.includes('@@') || v.nm.includes(lib.panel.softSplitter)) { | |
| 288 | + | multi = this.getAllCombinations(v.nm); | |
| 289 | + | multi_rem.push(i); | |
| 290 | + | multi.forEach(w => { | |
| 291 | + | multi_obj.push({ | |
| 292 | + | nm: w.join(''), | |
| 293 | + | item: this.copy(v.item), | |
| 294 | + | track: v.track, | |
| 295 | + | srt: lib.lib.sort(w.join('')) | |
| 296 | + | }); | |
| 297 | + | }); | |
| 298 | + | } | |
| 299 | + | }); | |
| 300 | + | i = multi_rem.length; | |
| 301 | + | while (i--) br.splice(multi_rem[i], 1); | |
| 302 | + | this.sort(multi_obj); | |
| 303 | + | multi_obj.forEach(v => { | |
| 304 | + | n = v.nm; | |
| 305 | + | nU = n.toUpperCase(); | |
| 306 | + | if (n_o != nU) { | |
| 307 | + | n_o = nU; | |
| 308 | + | multi_cond[j] = { | |
| 309 | + | nm: n, | |
| 310 | + | item: this.copy(v.item), | |
| 311 | + | track: v.track, | |
| 312 | + | srt: v.srt | |
| 313 | + | }; | |
| 314 | + | j++; | |
| 315 | + | } else v.item.forEach(v => multi_cond[j - 1].item.push(v)); | |
| 316 | + | }); | |
| 317 | + | br.forEach(v => { | |
| 318 | + | v.nm = v.nm.replace(Regex.LibMarkerMultiProcess, ''); | |
| 319 | + | nm_arr.push(v.nm); | |
| 320 | + | }); | |
| 321 | + | multi_cond.forEach((v, i) => { | |
| 322 | + | h = nm_arr.indexOf(v.nm); | |
| 323 | + | if (h != -1) { | |
| 324 | + | v.item.forEach(v => br[h].item.push(v)); | |
| 325 | + | multi_cond.splice(i, 1); | |
| 326 | + | } | |
| 327 | + | }); | |
| 328 | + | multi_cond.forEach((v, i) => br.splice(i + 1, 0, { | |
| 329 | + | nm: v.nm, | |
| 330 | + | sel: false, | |
| 331 | + | track: v.track, | |
| 332 | + | child: [], | |
| 333 | + | item: this.copy(v.item), | |
| 334 | + | srt: v.srt | |
| 335 | + | })); | |
| 336 | + | this.merge(br); | |
| 337 | + | break; | |
| 338 | + | } | |
| 339 | + | } | |
| 340 | + | this.sort(br); | |
| 341 | + | br[0].sorted = true; | |
| 342 | + | } | |
| 343 | + | const br_l = br.length; | |
| 344 | + | const par = this.tree.length - 1; | |
| 345 | + | if (level == 0) this.clearTree(); | |
| 346 | + | ||
| 347 | + | // * Apply View By Folder Hide if View by Folder Structure is active | |
| 348 | + | if (lib.panel.folderView) lib.men.setViewByFolderHide(this.tree, libSet.viewByFolderHide); | |
| 349 | + | ||
| 350 | + | br.forEach((v, i) => { | |
| 351 | + | j = this.tree.length; | |
| 352 | + | const item = this.tree[j] = v; | |
| 353 | + | item.top = !i; | |
| 354 | + | item.bot = i == br_l - 1; | |
| 355 | + | item.count = ''; | |
| 356 | + | item.ix = j; | |
| 357 | + | item.level = level; | |
| 358 | + | item.par = par; | |
| 359 | + | switch (true) { | |
| 360 | + | case libSet.facetView: | |
| 361 | + | if (!item.root) item.track = true; | |
| 362 | + | break; | |
| 363 | + | case l != -1 && !this.showTracks: | |
| 364 | + | this.range(item.item).some(v => { | |
| 365 | + | if (lib.lib.node[v] && (lib.lib.node[v].length == l + 1 || lib.lib.node[v].length == l + 2)) return item.track = true; | |
| 366 | + | }); | |
| 367 | + | break; | |
| 368 | + | case l == 0 && lib.lib.node[item.item[0].start] && lib.lib.node[item.item[0].start].length == 1: | |
| 369 | + | item.track = true; | |
| 370 | + | break; | |
| 371 | + | } | |
| 372 | + | if (lib.ui.col.counts && (!item.track || !this.showTracks)) { | |
| 373 | + | const str = `@!#${lib.ui.col.counts}\`${this.highlight.text ? lib.ui.col.text_h : lib.ui.col.counts}\`${lib.ui.col.textSel}@!#`; | |
| 374 | + | if (!item.nm.endsWith(str)) item.nm += str; | |
| 375 | + | } | |
| 376 | + | item.name = !lib.panel.noDisplay ? item.nm : item.nm.replace(Regex.LibMarkerNoDisplayContent, ''); | |
| 377 | + | if (v.child.length > 0) this.buildTree(v.child, level + 1, node, !!item.root); | |
| 378 | + | }); | |
| 379 | + | if (lib.ui.style.squareNode && lib.ui.col.line) { | |
| 380 | + | this.row.lineMax = []; | |
| 381 | + | this.tree.forEach(v => { | |
| 382 | + | const depth = !this.inlineRoot ? v.level : Math.max(v.level - 1, 0) | |
| 383 | + | this.row.lineMax[depth] = v.ix | |
| 384 | + | }); | |
| 385 | + | } | |
| 386 | + | if (this.rootNode == 3) this.tree[0].name = this.tree[0].child.length > 1 ? lib.panel.rootName.replace('#^^^^#', this.tree[0].child.length) : lib.panel.rootName1; | |
| 387 | + | lib.find.initials = null; | |
| 388 | + | if (!block) { | |
| 389 | + | lib.sbar.setRows(this.tree.length); | |
| 390 | + | lib.panel.treePaint(); | |
| 391 | + | } | |
| 392 | + | } | |
| 393 | + | ||
| 394 | + | butTooltipFont() { | |
| 395 | + | return [grFont.fontDefault, 15 * $Lib.scale * libSet.zoomTooltipBut / 100, 0]; | |
| 396 | + | } | |
| 397 | + | ||
| 398 | + | calcStatistics(v) { | |
| 399 | + | const key = `stat${this.getKey(v)}`; | |
| 400 | + | const type = lib.panel.search.txt ? 'search' : libSet.filterBy ? 'filter' : 'standard'; | |
| 401 | + | if (this.cache[type][key]) return this.cache[type][key].value; | |
| 402 | + | const handleList = new FbMetadbHandleList(); | |
| 403 | + | let items = []; | |
| 404 | + | this.addItems(items, v.item); | |
| 405 | + | items = [...new Set(items)].sort(this.numSort) | |
| 406 | + | items.some(w => { | |
| 407 | + | if (w >= lib.panel.list.Count) return true; | |
| 408 | + | handleList.Add(lib.panel.list[w]); | |
| 409 | + | }); | |
| 410 | + | let date = ''; | |
| 411 | + | let dates; | |
| 412 | + | let indices; | |
| 413 | + | let ln; | |
| 414 | + | let n; | |
| 415 | + | let tf; | |
| 416 | + | let value; | |
| 417 | + | let values; | |
| 418 | + | switch (libSet.itemShowStatistics) { | |
| 419 | + | case 1: // bitrate | |
| 420 | + | values = this.tf.bitrate.EvalWithMetadbs(handleList); | |
| 421 | + | if (values.length == 1) { | |
| 422 | + | value = Number(values[0]) || ''; | |
| 423 | + | } else { | |
| 424 | + | let lengths = FbTitleFormat('%length_seconds_fp%').EvalWithMetadbs(handleList) | |
| 425 | + | const total = values.map((v, i) => v * lengths[i]); | |
| 426 | + | let totals = total.map(v => parseFloat(v) || ''); | |
| 427 | + | totals = totals.filter((v, i) => v && lengths[i] ? v : lengths.splice(i, 1)); | |
| 428 | + | totals = totals.reduce((a, b) => a + b, 0); | |
| 429 | + | lengths = lengths.map(v => parseFloat(v)).reduce((a, b) => a + b, 0); | |
| 430 | + | value = Number(Math.round(totals / lengths)) || ''; | |
| 431 | + | } | |
| 432 | + | if (lib.panel.imgView && value) value = `${value} kbps`; | |
| 433 | + | this.cache[type][key] = { | |
| 434 | + | value, | |
| 435 | + | items | |
| 436 | + | } | |
| 437 | + | return value; | |
| 438 | + | case 2: { // duration | |
| 439 | + | const duration = utils.FormatDuration(handleList.CalcTotalDuration()); | |
| 440 | + | this.cache[type][key] = { | |
| 441 | + | value: duration, | |
| 442 | + | items: [] | |
| 443 | + | } | |
| 444 | + | return duration; | |
| 445 | + | } | |
| 446 | + | case 3: { // total size | |
| 447 | + | const bytes = this.tf.bytes.EvalWithMetadbs(handleList); | |
| 448 | + | let size = [...new Set(bytes)]; | |
| 449 | + | size = size.map(v => { | |
| 450 | + | const a = v.split('|'); | |
| 451 | + | return a[a.length - 1]; | |
| 452 | + | }); | |
| 453 | + | if (!size.length) return ''; | |
| 454 | + | size = size.map(v => parseInt(v)).reduce((a, b) => a + b, 0); | |
| 455 | + | const formattedBytes = this.formatBytes(size); | |
| 456 | + | this.cache[type][key] = { | |
| 457 | + | value: formattedBytes, | |
| 458 | + | items | |
| 459 | + | } | |
| 460 | + | return formattedBytes; | |
| 461 | + | } | |
| 462 | + | case 4: // rating | |
| 463 | + | case 5: // popularity | |
| 464 | + | tf = libSet.itemShowStatistics == 4 ? this.tf.rating : this.tf.popularity; | |
| 465 | + | values = tf.EvalWithMetadbs(handleList); | |
| 466 | + | values = this.getNumbers(values); | |
| 467 | + | values = values.filter(Boolean) | |
| 468 | + | ln = values.length; | |
| 469 | + | if (!ln) return ''; | |
| 470 | + | values = values.map(v => parseFloat(v)).reduce((a, b) => a + b, 0); | |
| 471 | + | value = Math.ceil(values / ln); | |
| 472 | + | if (lib.panel.imgView && this.label) value = (libSet.itemShowStatistics == 4 ? '等级 ' : '热门 ') + value; | |
| 473 | + | this.cache[type][key] = { | |
| 474 | + | value, | |
| 475 | + | items | |
| 476 | + | } | |
| 477 | + | return value; | |
| 478 | + | case 6: // date (first release) | |
| 479 | + | case 9: // firstPlayed | |
| 480 | + | case 10: // lastPlayed | |
| 481 | + | case 11: // added | |
| 482 | + | tf = | |
| 483 | + | libSet.itemShowStatistics == 6 ? this.tf.date : | |
| 484 | + | libSet.itemShowStatistics == 9 ? this.tf.firstPlayed : | |
| 485 | + | libSet.itemShowStatistics == 10 ? this.tf.lastPlayed : | |
| 486 | + | this.tf.added; | |
| 487 | + | dates = tf.EvalWithMetadbs(handleList); | |
| 488 | + | dates = dates.filter(v => v !== ''); | |
| 489 | + | ln = dates.length; | |
| 490 | + | if (ln) { | |
| 491 | + | if (ln == 1) date = dates[0]; | |
| 492 | + | else { | |
| 493 | + | date = libSet.itemShowStatistics == 6 || libSet.itemShowStatistics == 9 || libSet.itemShowStatistics == 11 ? | |
| 494 | + | dates.reduce((pre, cur) => Date.parse(pre) > Date.parse(cur) ? cur : pre) : | |
| 495 | + | dates.reduce((pre, cur) => Date.parse(cur) > Date.parse(pre) ? cur : pre) | |
| 496 | + | } | |
| 497 | + | } | |
| 498 | + | if (!date) return ''; | |
| 499 | + | if (lib.panel.imgView && this.label) date = ['', '', '', '', '', '', (v.root ? '首次发行 ' : ''), '', '', '首次播放 ', '最近播放 ', '添加时间 '][libSet.itemShowStatistics] + date; | |
| 500 | + | this.cache[type][key] = { | |
| 501 | + | value: date, | |
| 502 | + | items | |
| 503 | + | } | |
| 504 | + | return date; | |
| 505 | + | case 7: { // queue | |
| 506 | + | let index = ''; | |
| 507 | + | indices = []; | |
| 508 | + | const queueHandles = plman.GetPlaybackQueueHandles(); | |
| 509 | + | handleList.Convert().forEach(h => { | |
| 510 | + | const j = queueHandles.Find(h); | |
| 511 | + | if (j != -1) indices.push(j + 1); | |
| 512 | + | }); | |
| 513 | + | ln = indices.length; | |
| 514 | + | if (ln) { | |
| 515 | + | if (ln == 1) index = indices[0]; | |
| 516 | + | else { | |
| 517 | + | index = this.arrayToRange(indices); | |
| 518 | + | index.join(); | |
| 519 | + | } | |
| 520 | + | } | |
| 521 | + | if (!index) return ''; | |
| 522 | + | if (lib.panel.imgView && this.label) index = `Queue ${index}`; | |
| 523 | + | this.cache[type][key] = { | |
| 524 | + | value: index, | |
| 525 | + | items | |
| 526 | + | } | |
| 527 | + | return index; | |
| 528 | + | } | |
| 529 | + | case 8: { // playcount | |
| 530 | + | let playcount = this.tf.pc.EvalWithMetadbs(handleList); | |
| 531 | + | const played = [...new Set(playcount)]; | |
| 532 | + | n = played.map(v => { | |
| 533 | + | const a = v.split('|'); | |
| 534 | + | return a[a.length - 1]; | |
| 535 | + | }); | |
| 536 | + | playcount = this.getNumbers(n); | |
| 537 | + | if (!playcount.length) return ''; | |
| 538 | + | playcount = n.map(v => parseInt(v)).reduce((a, b) => a + b, 0); | |
| 539 | + | if (lib.panel.imgView) playcount = `${(this.label ? '已播放 ' : '') + playcount} 次`; | |
| 540 | + | this.cache[type][key] = { | |
| 541 | + | value: playcount, | |
| 542 | + | items | |
| 543 | + | } | |
| 544 | + | return playcount; | |
| 545 | + | } | |
| 546 | + | } | |
| 547 | + | } | |
| 548 | + | ||
| 549 | + | checkAutoHeight() { | |
| 550 | + | if (lib.panel.pn_h_auto && !lib.panel.imgView && libSet.pn_h == libSet.pn_h_min && this.tree[0]) this.clearChild(this.tree[0]); | |
| 551 | + | } | |
| 552 | + | ||
| 553 | + | check_ix(br, x, y, type) { | |
| 554 | + | if (lib.panel.imgView) return true; | |
| 555 | + | if (!br) return false; | |
| 556 | + | x -= lib.ui.x; | |
| 557 | + | const level = !this.inlineRoot ? br.level : Math.max(br.level - 1, 0); | |
| 558 | + | const icon_w = this.inlineRoot && br.ix == 0 ? 0 : lib.ui.icon.w + (!this.fullLineSelection ? lib.ui.l.wf : 0); | |
| 559 | + | return type ? (x >= Math.round(this.treeIndent * level + lib.ui.sz.margin) && x < Math.round(this.treeIndent * level + lib.ui.sz.margin) + br.w + icon_w) : | |
| 560 | + | (x >= Math.round(this.treeIndent * level + lib.ui.sz.margin) + icon_w) && x < Math.min(Math.round(this.treeIndent * level + lib.ui.sz.margin) + icon_w + br.w, lib.panel.tree.w); | |
| 561 | + | } | |
| 562 | + | ||
| 563 | + | checkNode(gr) { | |
| 564 | + | if (lib.sbar.draw_timer || this.nodeStyle != 7) return; | |
| 565 | + | try { | |
| 566 | + | lib.ui.style.symb.SetPartAndStateID(2, 1); | |
| 567 | + | lib.ui.style.symb.SetPartAndStateID(2, 2); | |
| 568 | + | lib.ui.style.symb.DrawThemeBackground(gr, -lib.ui.sz.node, -lib.ui.sz.node, lib.ui.sz.node, lib.ui.sz.node); | |
| 569 | + | } catch (e) { | |
| 570 | + | libSet.nodeStyle = 0; | |
| 571 | + | this.nodeStyle = 0; | |
| 572 | + | } | |
| 573 | + | } | |
| 574 | + | ||
| 575 | + | checkRow(x, y) { | |
| 576 | + | this.m.br = -1; | |
| 577 | + | const im = this.get_ix(x, y, true, false); | |
| 578 | + | if (im >= this.tree.length || im < 0) return -1; | |
| 579 | + | if (lib.panel.imgView) return im; | |
| 580 | + | const item = this.tree[im]; | |
| 581 | + | const level = !this.inlineRoot ? item.level : Math.max(item.level - 1, 0); | |
| 582 | + | if (x < Math.round(this.treeIndent * level) + lib.ui.icon.w + lib.ui.sz.margin + lib.ui.x + lib.ui.w && (!item.track || item.root)) this.m.br = im; | |
| 583 | + | return im; | |
| 584 | + | } | |
| 585 | + | ||
| 586 | + | check_tooltip(ix, x, y) { | |
| 587 | + | if (this.lbtnDn || lib.sbar.draw_timer) return; | |
| 588 | + | const item = this.tree[ix]; | |
| 589 | + | let text = ''; | |
| 590 | + | if (!item) return; | |
| 591 | + | switch (true) { | |
| 592 | + | case !lib.panel.imgView: { | |
| 593 | + | const trace1 = item.tt && item.tt.needed && x >= item.tt.x && x <= item.tt.x + item.tt.w && y >= item.tt.y && y <= item.tt.y + lib.ui.row.h; | |
| 594 | + | const trace2 = item.stats_tt && item.stats_tt.needed && x >= item.stats_tt.x + item.stats_tt.w && x <= lib.ui.w - lib.ui.sz.marginRight && y >= item.stats_tt.y && y <= item.stats_tt.y + lib.ui.row.h * 0.9; | |
| 595 | + | if (trace2) { | |
| 596 | + | text = this.statisticsShow ? | |
| 597 | + | (item.statistics !== undefined ? `${this.statistics[this.statisticsShow]}: ${item.statistics}` : '') : | |
| 598 | + | (item.count ? `${['', '首', '项'][this.nodeCounts]}:${item.count}` : ''); | |
| 599 | + | } else if (trace1) { | |
| 600 | + | text = (!lib.panel.colMarker ? item.name : item.name.replace(Regex.LibMarkerColor, '')) + (!this.countsRight || this.statisticsShow ? item.count : ''); | |
| 601 | + | text = text.replace(/&/g, '&&'); | |
| 602 | + | } | |
| 603 | + | if (text != libTooltip.Text) this.deactivateTooltip(); | |
| 604 | + | if (!trace1 && !trace2 || !item.tt && !item.stats_tt) { | |
| 605 | + | this.deactivateTooltip(); | |
| 606 | + | return; | |
| 607 | + | } | |
| 608 | + | break; | |
| 609 | + | } | |
| 610 | + | case lib.panel.imgView: { | |
| 611 | + | let trace1 = false; | |
| 612 | + | let trace2 = false; | |
| 613 | + | let trace3 = false; | |
| 614 | + | if (!libImg.labels.hide) { | |
| 615 | + | if (!item.tt) { | |
| 616 | + | this.deactivateTooltip(); | |
| 617 | + | return; | |
| 618 | + | } | |
| 619 | + | trace1 = x >= item.tt.x && x <= item.tt.x + item.tt.w && y >= item.tt.y1 && y <= item.tt.y1 + libImg.text.h; | |
| 620 | + | trace2 = item.tt.y2 == -1 ? false : x >= item.tt.x && x <= item.tt.x + item.tt.w && y >= item.tt.y2 && y <= item.tt.y2 + libImg.text.h; | |
| 621 | + | trace3 = item.tt.y3 == -1 ? false : x >= item.tt.x && x <= item.tt.x + item.tt.w && y >= item.tt.y3 && y <= item.tt.y3 + libImg.text.h; | |
| 622 | + | text = trace1 || trace2 || trace3 ? item.tt.text : ''; | |
| 623 | + | if (lib.panel.colMarker) text = text.replace(Regex.LibMarkerColor, ''); | |
| 624 | + | text = text.replace(/&/g, '&&'); | |
| 625 | + | if (text != libTooltip.Text) this.deactivateTooltip(); | |
| 626 | + | if (!trace1 && !trace2 && !trace3 || !item.tt[1] && !item.tt[2] && !item.tt[3]) { | |
| 627 | + | this.deactivateTooltip(); | |
| 628 | + | return; | |
| 629 | + | } | |
| 630 | + | } else { | |
| 631 | + | text = lib.panel.lines == 2 ? !libSet.albumArtFlipLabels ? `${item.grp}\n${item.lot}` : `${item.lot}\n${item.grp}` : item.grp; | |
| 632 | + | if (lib.panel.colMarker) text = text.replace(Regex.LibMarkerColor, ''); | |
| 633 | + | text = text.replace(/&/g, '&&'); | |
| 634 | + | if (text != libTooltip.Text) this.deactivateTooltip(); | |
| 635 | + | } | |
| 636 | + | break; | |
| 637 | + | } | |
| 638 | + | } | |
| 639 | + | this.activateTooltip(text); | |
| 640 | + | lib.timer.tooltipLib(); | |
| 641 | + | } | |
| 642 | + | ||
| 643 | + | checkTooltip(item, x, y, txt_w, w) { | |
| 644 | + | item.tt = { | |
| 645 | + | needed: txt_w > w, | |
| 646 | + | x, | |
| 647 | + | y, | |
| 648 | + | w | |
| 649 | + | }; | |
| 650 | + | item.stats_tt = { | |
| 651 | + | needed: !this.tooltipStatistics || !this.statisticsShow || item.root ? false : [false, true, false, true, true, true, true, true, true, true, true, true][this.statisticsShow] && item.statistics !== undefined, | |
| 652 | + | x, | |
| 653 | + | y: y + lib.ui.row.h * 0.1, | |
| 654 | + | w | |
| 655 | + | }; | |
| 656 | + | } | |
| 657 | + | ||
| 658 | + | checkTooltipFont(type) { | |
| 659 | + | switch (type) { | |
| 660 | + | case 'btn': { | |
| 661 | + | const newTooltipFont = this.butTooltipFont(); | |
| 662 | + | if ($Lib.equal(this.cur, newTooltipFont)) return; | |
| 663 | + | this.cur = newTooltipFont; | |
| 664 | + | break; | |
| 665 | + | } | |
| 666 | + | case 'tree': { | |
| 667 | + | const newTooltipFont = this.treeTooltipFont(); | |
| 668 | + | if ($Lib.equal(this.cur, newTooltipFont)) return; | |
| 669 | + | this.cur = newTooltipFont; | |
| 670 | + | break; | |
| 671 | + | } | |
| 672 | + | } | |
| 673 | + | libTooltip.SetFont(this.cur[0], this.cur[1], this.cur[2]); | |
| 674 | + | } | |
| 675 | + | ||
| 676 | + | clearSelected() { | |
| 677 | + | this.tree.forEach(v => v.sel = false); | |
| 678 | + | } | |
| 679 | + | ||
| 680 | + | clearTree() { | |
| 681 | + | if (lib.panel.imgView && this.tree.length) libImg.trimCache(this.tree[0].key); | |
| 682 | + | this.tree = []; | |
| 683 | + | } | |
| 684 | + | ||
| 685 | + | clearChild(br) { | |
| 686 | + | br.child = []; | |
| 687 | + | this.buildTree(lib.lib.root, 0, true, true); | |
| 688 | + | } | |
| 689 | + | ||
| 690 | + | clickedOn(x, y, item) { | |
| 691 | + | if (lib.panel.imgView) return 'text'; | |
| 692 | + | if (this.inlineRoot && item.ix == 0) return this.check_ix(item, x, y, false) ? 'text' : 'none'; | |
| 693 | + | const level = !this.inlineRoot ? item.level : Math.max(item.level - 1, 0); | |
| 694 | + | return x < lib.ui.x + Math.round(this.treeIndent * level) + lib.ui.icon.w + lib.ui.sz.margin ? 'node' : this.check_ix(item, x, y, false) ? 'text' : 'none'; | |
| 695 | + | } | |
| 696 | + | ||
| 697 | + | collapseAll() { | |
| 698 | + | let ic = this.get_ix(lib.ui.x, lib.ui.y + lib.panel.tree.y + lib.ui.row.h / 2, true, false); | |
| 699 | + | if (ic >= this.tree.length || ic < 0) return; | |
| 700 | + | let j = this.tree[ic].level; | |
| 701 | + | if (this.rootNode) j -= 1; | |
| 702 | + | if (this.tree[ic].level != 0) { | |
| 703 | + | const par = this.tree[ic].par; | |
| 704 | + | const pr_pr = []; | |
| 705 | + | for (let m = 1; m < j + 1; m++) { | |
| 706 | + | pr_pr[m] = m == 1 ? par : this.tree[pr_pr[m - 1]].par; | |
| 707 | + | ic = pr_pr[m]; | |
| 708 | + | } | |
| 709 | + | } | |
| 710 | + | const nm = this.tree[ic].srt[0].toUpperCase(); | |
| 711 | + | this.tree.forEach(v => { | |
| 712 | + | if (!v.root) v.child = []; | |
| 713 | + | }); | |
| 714 | + | this.buildTree(lib.lib.root, 0); | |
| 715 | + | let scr_pos = false; | |
| 716 | + | this.tree.some((v, i) => { | |
| 717 | + | if (v.srt[0].toUpperCase() == nm) { | |
| 718 | + | lib.sbar.checkScroll(i * lib.ui.row.h); | |
| 719 | + | return scr_pos = true; | |
| 720 | + | } | |
| 721 | + | }); | |
| 722 | + | if (!scr_pos) { | |
| 723 | + | lib.sbar.reset(); | |
| 724 | + | lib.panel.treePaint(); | |
| 725 | + | } | |
| 726 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 727 | + | } | |
| 728 | + | ||
| 729 | + | condense(child) { | |
| 730 | + | child.forEach(v => { | |
| 731 | + | if (typeof v.item[0] !== 'number') return; | |
| 732 | + | v.item = this.createRanges(v.item); | |
| 733 | + | }); | |
| 734 | + | } | |
| 735 | + | ||
| 736 | + | copy(item) { | |
| 737 | + | return item.map(v => v); | |
| 738 | + | } | |
| 739 | + | ||
| 740 | + | createImages() { | |
| 741 | + | if (!lib.ui.w || !lib.ui.h) return; | |
| 742 | + | if (!this.nodeStyle) { | |
| 743 | + | const sz = lib.ui.sz.node; | |
| 744 | + | const ln_w = Math.max(Math.floor(sz / 9), 1); | |
| 745 | + | let plus = true; | |
| 746 | + | let hot = false; | |
| 747 | + | let sy_w = ln_w; | |
| 748 | + | const x = 0; | |
| 749 | + | const y = 0; | |
| 750 | + | if (((sz - ln_w * 3) / 2) % 1 != 0) sy_w = ln_w > 1 ? ln_w - 1 : ln_w + 1; | |
| 751 | + | for (let j = 0; j < 4; j++) { | |
| 752 | + | this.nd[j] = $Lib.gr(sz, sz, true, g => { | |
| 753 | + | hot = j > 1; | |
| 754 | + | plus = !j || j == 2; | |
| 755 | + | if (grSet.libraryDesign !== 'reborn') { | |
| 756 | + | g.FillSolidRect(x, y, sz, sz, RGB(145, 145, 145)); | |
| 757 | + | if (!hot) FillGradRect(g, x + ln_w, y + ln_w, sz - ln_w * 2, sz - ln_w * 2, 91, plus ? lib.ui.col.icon_e[0] : lib.ui.col.icon_c[0], plus ? lib.ui.col.icon_e[1] : lib.ui.col.icon_c[1], 1.0); | |
| 758 | + | else FillGradRect(g, x + ln_w, y + ln_w, sz - ln_w * 2, sz - ln_w * 2, 91, lib.ui.col.icon_h[0], lib.ui.col.icon_h[1], 1.0); | |
| 759 | + | // const x_o = [x, x + sz - ln_w, x, x + sz - ln_w]; | |
| 760 | + | // const y_o = [y, y, y + sz - ln_w, y + sz - ln_w]; | |
| 761 | + | for (let i = 0; i < 4; i++) { // g.FillSolidRect(x_o[i], y_o[i], ln_w, ln_w, RGB(186, 187, 188)); | |
| 762 | + | if (grSet.libraryDesign === 'traditional') g.FillSolidRect(x, y, sz, sz, lib.ui.col.iconPlusBg); | |
| 763 | + | } | |
| 764 | + | } else if (libSet.nodeStyle === 0) { | |
| 765 | + | g.DrawRect(x, y, sz - 1, sz - 1, 1, lib.ui.col.iconPlusBg); | |
| 766 | + | } | |
| 767 | + | if (plus) g.FillSolidRect(Math.floor(x + (sz - sy_w) / 2), y + ln_w + Math.min(ln_w, sy_w), sy_w, sz - ln_w * 2 - Math.min(ln_w, sy_w) * 2, !hot ? lib.ui.col.iconPlus : lib.ui.col.iconPlus_h); | |
| 768 | + | g.FillSolidRect(x + ln_w + Math.min(ln_w, sy_w), Math.floor(y + (sz - sy_w) / 2), sz - ln_w * 2 - Math.min(ln_w, sy_w) * 2, sy_w, !hot ? (plus ? lib.ui.col.iconMinus_e : lib.ui.col.iconMinus_c) : lib.ui.col.iconMinus_h); | |
| 769 | + | }); | |
| 770 | + | } | |
| 771 | + | } else { | |
| 772 | + | let lightCol = lib.ui.isLightCol(lib.ui.col.icon_h); | |
| 773 | + | $Lib.gr(1, 1, false, g => { | |
| 774 | + | const h = this.nodeStyle != 7 ? g.CalcTextHeight('String', lib.ui.icon.font) / 15 : g.CalcTextHeight('String', lib.ui.font.main) / 20; | |
| 775 | + | this.sy_sz = Math.floor(Math.max(8 * libSet.zoomNode / 100 * h, 5)); | |
| 776 | + | }); | |
| 777 | + | ||
| 778 | + | const sz = Math.max(Math.round(this.sy_sz * 1.666667), 1); | |
| 779 | + | this.triangle.highlight = $Lib.gr(sz, sz, true, g => { | |
| 780 | + | g.SetSmoothingMode(4); | |
| 781 | + | g.FillPolygon(lib.ui.col.icon_h, 1, [sz, 0, sz, sz, 0, sz]); | |
| 782 | + | g.SetSmoothingMode(0); | |
| 783 | + | }); | |
| 784 | + | lightCol = lib.ui.isLightCol(lib.ui.col.icon_e); | |
| 785 | + | this.triangle.expand = $Lib.gr(sz, sz, true, g => { | |
| 786 | + | g.SetSmoothingMode(4); | |
| 787 | + | g.FillPolygon(lib.ui.col.icon_e & (lightCol ? 0xC0ffffff : 0xBAffffff), 1, [sz, 0, sz, sz, 0, sz]); | |
| 788 | + | g.SetSmoothingMode(0); | |
| 789 | + | }); | |
| 790 | + | this.triangle.select = $Lib.gr(sz, sz, true, g => { | |
| 791 | + | g.SetSmoothingMode(4); | |
| 792 | + | g.FillPolygon(lib.ui.col.textSel & (lightCol ? 0xC0ffffff : 0xBAffffff), 1, [sz, 0, sz, sz, 0, sz]); | |
| 793 | + | g.SetSmoothingMode(0); | |
| 794 | + | }); | |
| 795 | + | } | |
| 796 | + | } | |
| 797 | + | ||
| 798 | + | createRanges(arr) { | |
| 799 | + | const ret = []; | |
| 800 | + | let start; | |
| 801 | + | let end; | |
| 802 | + | for (let i = 0; i < arr.length; i++) { | |
| 803 | + | start = end = arr[i]; | |
| 804 | + | while (arr[i + 1] == end + 1) { | |
| 805 | + | end++; | |
| 806 | + | i++; | |
| 807 | + | } | |
| 808 | + | ret.push(start == end ? { | |
| 809 | + | start, | |
| 810 | + | end: start, | |
| 811 | + | count: 1 | |
| 812 | + | } : { | |
| 813 | + | start, | |
| 814 | + | end, | |
| 815 | + | count: end - start + 1 | |
| 816 | + | }); | |
| 817 | + | } | |
| 818 | + | return ret; | |
| 819 | + | } | |
| 820 | + | ||
| 821 | + | cusCol(gr, text, item, item_x, item_y, w, h, type, np, font, ellipsisSpace, cus) { | |
| 822 | + | if (!text) return; | |
| 823 | + | let col = []; | |
| 824 | + | let col_x = []; | |
| 825 | + | let col_w = []; | |
| 826 | + | let w_arr = []; | |
| 827 | + | let x = 0; | |
| 828 | + | if (item[cus] && item[cus].id == this.id && !this.highlight.nowPlayingIndicator) { | |
| 829 | + | col = item[cus].col; | |
| 830 | + | col_x = item[cus].col_x; | |
| 831 | + | col_w = item[cus].col_w; | |
| 832 | + | text = item[cus].txt; | |
| 833 | + | w_arr = item[cus].txt_w; | |
| 834 | + | } else { | |
| 835 | + | text = text.split('@!#'); | |
| 836 | + | text.forEach((v, i) => { | |
| 837 | + | if (i % 2 == 0) w_arr[i] = gr.CalcTextWidth(text[i], font); | |
| 838 | + | }); | |
| 839 | + | text.forEach((v, i) => { | |
| 840 | + | if (i % 2 == 0) { | |
| 841 | + | const cur_w = x + w_arr[i]; | |
| 842 | + | const next_text = !!text[i + 2]; | |
| 843 | + | let ellipsis_corr = 0; | |
| 844 | + | const roomForEllipsis = !(next_text && cur_w < w && w - cur_w < ellipsisSpace); | |
| 845 | + | if (!roomForEllipsis) { | |
| 846 | + | text[i + 2] = ''; | |
| 847 | + | ellipsis_corr = ellipsisSpace; | |
| 848 | + | } | |
| 849 | + | col[i] = i > 0 ? (text[i - 1]).split('`') : (!lib.panel.imgView || !libImg.labels.overlayDark ? lib.ui.col.txtArr : [RGB(240, 240, 240), lib.ui.col.text_h, lib.ui.col.text]); | |
| 850 | + | col_x[i] = x; | |
| 851 | + | col_w[i] = w - x - ellipsis_corr > ellipsis_corr ? w - x - ellipsis_corr : w - x; | |
| 852 | + | x += w_arr[i]; | |
| 853 | + | } | |
| 854 | + | }); | |
| 855 | + | item[cus] = { | |
| 856 | + | id: this.id, | |
| 857 | + | txt: text, | |
| 858 | + | col, | |
| 859 | + | col_x, | |
| 860 | + | col_w, | |
| 861 | + | txt_w: w_arr | |
| 862 | + | }; | |
| 863 | + | } | |
| 864 | + | text.forEach((v, i) => { | |
| 865 | + | if (i % 2 == 0 && text[i]) { | |
| 866 | + | gr.GdiDrawText(text[i], font, !np ? col[i][type] : lib.ui.col.nowp, item_x + col_x[i], item_y, col_w[i], h, lib.panel.lc); | |
| 867 | + | } | |
| 868 | + | }); | |
| 869 | + | } | |
| 870 | + | ||
| 871 | + | deactivateTooltip() { | |
| 872 | + | if (!libTooltip.Text && !grSet.showStyledTooltips || lib.but.trace) return; | |
| 873 | + | libTooltip.Text = ''; | |
| 874 | + | lib.but.tooltipLib.delay = false; | |
| 875 | + | libTooltip.Deactivate(); | |
| 876 | + | } | |
| 877 | + | ||
| 878 | + | dragDrop(x, y) { | |
| 879 | + | x -= lib.ui.x; y -= lib.ui.y; | |
| 880 | + | if (!this.lbtnDn) return; | |
| 881 | + | const drag_diff = !libSet.touchControl ? Math.sqrt((Math.pow(this.last_pressed_coord.x - x, 2) + Math.pow(this.last_pressed_coord.y - y, 2))) : Math.abs(x - this.last_pressed_coord.x); | |
| 882 | + | if (drag_diff > 7) { | |
| 883 | + | if (libSet.touchControl) { | |
| 884 | + | const ix = this.get_ix(x, y, true, false); | |
| 885 | + | const item = this.tree[ix]; | |
| 886 | + | if (lib.ui.id.dragDrop != ix || ix >= this.tree.length || ix < 0) return; | |
| 887 | + | if (!item.sel && !lib.vk.k('ctrl')) this.setTreeSel(ix, item.sel); | |
| 888 | + | } | |
| 889 | + | this.last_pressed_coord = { | |
| 890 | + | x: undefined, | |
| 891 | + | y: undefined | |
| 892 | + | }; | |
| 893 | + | ||
| 894 | + | const handleList = this.getHandleList('newItems'); | |
| 895 | + | this.sortIfNeeded(handleList); | |
| 896 | + | ||
| 897 | + | if (grm.ui.displayLibrarySplit()) { // * Drag and drop action from Library to Playlist in split layout | |
| 898 | + | grm.ui.librarySplitDragDrop(handleList); | |
| 899 | + | } else { | |
| 900 | + | fb.DoDragDrop(0, handleList, handleList.Count ? 1 | 4 : 0); | |
| 901 | + | } | |
| 902 | + | ||
| 903 | + | this.lbtnDn = false; | |
| 904 | + | } | |
| 905 | + | } | |
| 906 | + | ||
| 907 | + | draw(gr) { // * Heavily modified | |
| 908 | + | if (lib.lib.empty) return gr.DrawString(lib.lib.empty, lib.ui.font.main, lib.ui.col.text, lib.ui.x, grm.ui.wh * 0.5 - lib.ui.y - lib.panel.search.h * 0.5, lib.panel.tree.w, lib.ui.row.h * 3, Stringformat.Align_Center); | |
| 909 | + | if (!this.tree.length || !lib.panel.draw) return gr.GdiDrawText(this.libItems && !lib.panel.search.txt && !libSet.filterBy && libSet.libSource ? '加载中...\n\n' : lib.lib.none, lib.ui.font.main, lib.ui.col.text, lib.ui.x + lib.ui.sz.margin, lib.ui.y + lib.panel.search.h, lib.panel.tree.w, lib.ui.row.h * 3); | |
| 910 | + | if (lib.panel.imgView) return; | |
| 911 | + | const b = $Lib.clamp(Math.round(lib.sbar.delta / lib.ui.row.h + 0.4), 0, this.tree.length - 1); | |
| 912 | + | // const bar_x = this.nodeStyle && this.nodeStyle < 5 ? 0 : lib.ui.sz.pad; | |
| 913 | + | const f = Math.min(b + lib.panel.rows, this.tree.length); | |
| 914 | + | const nm = []; | |
| 915 | + | const nowp_c = []; | |
| 916 | + | const updatedNowpBg = pl.col.header_nowplaying_bg !== null; // * Wait until nowplaying bg has a new color to prevent flashing | |
| 917 | + | const colNowPlaying = grSet.libraryBgImg ? RGBtoRGBA(lib.ui.col.nowPlayingBg, grSet.libraryBgRowOpacity) : lib.ui.col.nowPlayingBg; | |
| 918 | + | const colRowStripes = grSet.libraryBgImg ? RGBtoRGBA(lib.ui.col.rowStripes, grSet.libraryBgRowOpacity) : lib.ui.col.rowStripes; | |
| 919 | + | const row = []; | |
| 920 | + | const y1 = Math.round(lib.panel.search.h - lib.sbar.delta + lib.panel.node_y) + Math.floor(lib.ui.sz.node / 2); | |
| 921 | + | let i = 0; | |
| 922 | + | let item_x = 0; | |
| 923 | + | let item_y = 0; | |
| 924 | + | let sel_x = 0; | |
| 925 | + | let sel_w = 0; | |
| 926 | + | let level = 0; | |
| 927 | + | this.checkNode(gr); | |
| 928 | + | if (!lib.ui.style.squareNode) gr.SetTextRenderingHint(5); | |
| 929 | + | this.rows = 0; | |
| 930 | + | if (lib.ui.style.squareNode && lib.ui.col.line) { | |
| 931 | + | level = !this.inlineRoot ? this.tree[b].level : Math.max(this.tree[b].level - 1, 0); | |
| 932 | + | for (let j = 0; j <= level; j++) row[j] = b; | |
| 933 | + | } | |
| 934 | + | for (i = b; i < f; i++) { | |
| 935 | + | const item = this.tree[i]; | |
| 936 | + | this.getItemCount(item); | |
| 937 | + | nm[i] = item.name + (i || this.rootNode != 3 || this.nodeCounts == 1 && (this.countsRight || this.statisticsShow) ? (!this.countsRight || this.statisticsShow ? item.count : '') : ''); | |
| 938 | + | const counts = !this.statisticsShow ? item.count : item.statistics; | |
| 939 | + | if (this.highlight.nowPlayingShow && !item.root && this.inRange(this.nowp, item.item)) nowp_c.push(i); | |
| 940 | + | item.np = nowp_c.includes(i) || lib.panel.textDiffHighlight && this.m.i == i ? `${Unicode.BeamedEighthNotes} ` : ''; | |
| 941 | + | if (item.np && item.id != this.id) this.row.note_w = gr.CalcTextWidth(item.np, lib.ui.font.main); | |
| 942 | + | if (item.id != this.id || this.highlight.nowPlayingIndicator) { | |
| 943 | + | let itemName = !lib.panel.colMarker ? nm[i] : nm[i].replace(Regex.LibMarkerColor, ''); | |
| 944 | + | if (item.np && this.highlight.nowPlayingIndicator && item.track) itemName = item.np + itemName; | |
| 945 | + | item.name_w = gr.CalcTextWidth(itemName, lib.ui.font.main, true); | |
| 946 | + | item.count_w = this.nodeCounts && this.countsRight || this.statisticsShow ? | |
| 947 | + | gr.CalcTextWidth(counts || '000', lib.ui.font.small) + (counts ? lib.ui.row.h * 0.2 : 0) : 0; | |
| 948 | + | if (!this.fullLineSelection) { | |
| 949 | + | item.w = item.name_w; | |
| 950 | + | item.id = this.id; | |
| 951 | + | } | |
| 952 | + | } | |
| 953 | + | ||
| 954 | + | level = !this.inlineRoot ? item.level : Math.max(item.level - 1, 0); | |
| 955 | + | if (this.highlight.nowPlaying && !item.root && this.inRange(this.nowp, item.item)) nowp_c.push(i); | |
| 956 | + | item_y = Math.round(lib.ui.y + lib.ui.row.h * i + lib.panel.search.h - lib.sbar.delta); | |
| 957 | + | if (item_y < lib.panel.filter.y) { | |
| 958 | + | this.rows++; | |
| 959 | + | if (this.rowStripes) { | |
| 960 | + | const width = lib.sbar.w && libSet.sbarShow !== 0 ? lib.ui.w - SCALE(42) : lib.ui.w + 1; | |
| 961 | + | if (i % 2 == 0) gr.FillSolidRect(lib.ui.x, item_y + 1, width, lib.ui.row.h - 2, colRowStripes /*ui.col.bg1*/); | |
| 962 | + | else gr.FillSolidRect(lib.ui.x, item_y, width, lib.ui.row.h, lib.ui.col.bg2); | |
| 963 | + | } | |
| 964 | + | if ((item.sel || this.highlight.nowPlaying) && (lib.ui.col.bgSel != 0 || grCol.primary != 0)) { | |
| 965 | + | const icon_w = !this.inlineRoot || i ? lib.ui.icon.w : 0; | |
| 966 | + | item_x = Math.round(lib.ui.x + this.treeIndent * level + lib.ui.sz.margin) + icon_w; | |
| 967 | + | sel_x = Math.round(item_x - lib.ui.sz.sel); | |
| 968 | + | if (this.inlineRoot && !i) sel_x = Math.max(sel_x - lib.ui.sz.sel, 0); | |
| 969 | + | sel_w = Math.min(item.name_w + lib.ui.sz.sel * 2, lib.ui.x + lib.panel.tree.w - sel_x - item.count_w - 1); | |
| 970 | + | if (this.fullLineSelection) { | |
| 971 | + | sel_x = lib.ui.x; | |
| 972 | + | sel_w = lib.sbar.w && libSet.sbarShow !== 0 ? lib.ui.w - SCALE(42) : lib.ui.w + 1; | |
| 973 | + | } | |
| 974 | + | if (!nowp_c.includes(i)) { | |
| 975 | + | if (this.fullLineSelection && this.sbarShow === 1 && lib.ui.sbar.type === 2 && (this.highlight.row || !this.fullLineSelection)) { | |
| 976 | + | gr.FillSolidRect(sel_x, item_y, sel_w + lib.ui.l.w, lib.ui.row.h, lib.ui.col.bgSel); | |
| 977 | + | gr.FillSolidRect(sel_x, item_y, sel_w + lib.ui.l.w, lib.ui.l.w, lib.ui.col.bgSelframe); | |
| 978 | + | gr.FillSolidRect(sel_x, item_y + lib.ui.row.h, sel_w + lib.ui.l.w, lib.ui.l.w, lib.ui.col.bgSelframe); | |
| 979 | + | } | |
| 980 | + | } | |
| 981 | + | // * Now playing bg selection | |
| 982 | + | else if (this.highlight.nowPlaying && updatedNowpBg) { | |
| 983 | + | gr.FillSolidRect(grSet.libraryDesign === 'traditional' ? item_x - SCALE(2) : lib.ui.x, item_y, grSet.libraryDesign === 'traditional' && this.fullLineSelection ? sel_w - item_x - lib.ui.sz.margin - lib.ui.sz.node + lib.ui.l.w : grSet.libraryDesign === 'traditional' && !this.fullLineSelection ? sel_w + sel_x - item_x + SCALE(2) : !this.fullLineSelection ? sel_w + lib.ui.sz.margin + sel_x - lib.ui.x - lib.ui.sz.sideMarker : sel_w, lib.ui.row.h, colNowPlaying); | |
| 984 | + | ||
| 985 | + | if (grSet.libraryDesign !== 'traditional') { | |
| 986 | + | gr.FillSolidRect(lib.ui.x, item_y, lib.ui.sz.sideMarker, lib.ui.row.h, lib.ui.col.sideMarker); | |
| 987 | + | } | |
| 988 | + | } | |
| 989 | + | // * Marker selection with now playing active | |
| 990 | + | if (item.sel && this.highlight.nowPlaying) { | |
| 991 | + | if (grSet.libraryDesign !== 'traditional') { | |
| 992 | + | if (!this.inRange(this.nowp, item.item) || item.root) gr.DrawRect(this.fullLineSelection ? sel_x : lib.ui.x, item_y, this.fullLineSelection ? sel_w - 1 : sel_w + lib.ui.sz.margin + sel_x - lib.ui.x - lib.ui.sz.sideMarker, lib.ui.row.h, 1, lib.ui.col.selectionFrame); | |
| 993 | + | gr.FillSolidRect(lib.ui.x, this.highlight.nowPlaying && !item.root && this.inRange(this.nowp, item.item) ? item_y + 1 : item_y, lib.ui.sz.sideMarker, this.highlight.nowPlaying && !item.root && this.inRange(this.nowp, item.item) ? lib.ui.row.h - 1 : lib.ui.row.h + 1, lib.ui.col.sideMarker); | |
| 994 | + | } | |
| 995 | + | else if (grSet.libraryDesign === 'traditional') { | |
| 996 | + | gr.FillSolidRect(item_x - SCALE(2), item_y, grSet.libraryDesign === 'traditional' && this.fullLineSelection ? sel_w - item_x - lib.ui.sz.margin - lib.ui.sz.node + lib.ui.l.w : sel_w, lib.ui.row.h, colNowPlaying); | |
| 997 | + | } | |
| 998 | + | } | |
| 999 | + | // * Marker selection with now playing deactivated | |
| 1000 | + | if (item.sel && !this.highlight.nowPlaying && updatedNowpBg) { | |
| 1001 | + | gr.FillSolidRect(grSet.libraryDesign === 'traditional' && this.fullLineSelection ? item_x - SCALE(2) : sel_x, item_y, grSet.libraryDesign === 'traditional' && this.fullLineSelection ? sel_w - item_x - lib.ui.sz.margin - lib.ui.sz.node + lib.ui.l.w : sel_w, lib.ui.row.h, colNowPlaying); | |
| 1002 | + | ||
| 1003 | + | if (grSet.libraryDesign !== 'traditional') { | |
| 1004 | + | gr.FillSolidRect(lib.ui.x, item_y, lib.ui.w, lib.ui.row.h, colNowPlaying); | |
| 1005 | + | gr.FillSolidRect(lib.ui.x, item_y, lib.ui.sz.sideMarker, lib.ui.row.h, lib.ui.col.sideMarker); | |
| 1006 | + | } | |
| 1007 | + | } | |
| 1008 | + | } | |
| 1009 | + | } | |
| 1010 | + | } | |
| 1011 | + | for (i = b; i < f; i++) { | |
| 1012 | + | const item = this.tree[i]; | |
| 1013 | + | const level = !this.inlineRoot ? item.level : Math.max(item.level - 1, 0); | |
| 1014 | + | item_y = Math.round(lib.ui.y + lib.ui.row.h * i + lib.panel.search.h - lib.sbar.delta); | |
| 1015 | + | if (item_y < lib.panel.filter.y) { | |
| 1016 | + | item_x = Math.round(lib.ui.x + this.treeIndent * level + lib.ui.sz.margin); | |
| 1017 | + | if (this.inlineRoot && !item.level) item_x = lib.ui.x + lib.ui.sz.marginSearch; | |
| 1018 | + | if ((this.fullLineSelection && this.row.i == i || this.m.i == i)) { | |
| 1019 | + | sel_x = Math.round(item_x - lib.ui.sz.sel); | |
| 1020 | + | if (!this.inlineRoot || item.level) sel_x += lib.ui.icon.w; | |
| 1021 | + | sel_w = Math.min(item.name_w + lib.ui.sz.sel * 2, lib.ui.x + lib.panel.tree.w - sel_x - item.count_w - 1); | |
| 1022 | + | if (this.fullLineSelection) { | |
| 1023 | + | sel_x = lib.ui.x; | |
| 1024 | + | sel_w = lib.sbar.w ? lib.ui.w - SCALE(42) : lib.ui.w + 1; | |
| 1025 | + | } | |
| 1026 | + | if (this.highlight.row == 3 && (this.fullLineSelection && this.sbarShow == 1 && lib.ui.sbar.type == 2)) { | |
| 1027 | + | gr.DrawLine(sel_x, item_y, sel_w, item_y, lib.ui.l.w, lib.ui.col.frame); | |
| 1028 | + | gr.DrawLine(sel_x, item_y + lib.ui.row.h, sel_w, item_y + lib.ui.row.h, lib.ui.l.w, lib.ui.col.frame); | |
| 1029 | + | } | |
| 1030 | + | } | |
| 1031 | + | ||
| 1032 | + | if (lib.ui.style.squareNode && lib.ui.col.line) { | |
| 1033 | + | if (item.top) row[level] = i; | |
| 1034 | + | const ff = lib.sbar.rows_drawn == this.rows || i < lib.sbar.rows_drawn ? f : f - 1; | |
| 1035 | + | if (item.bot || i === ff - 1) { | |
| 1036 | + | for (let depth = (i === ff - 1 ? 0 : level); depth <= level; depth++) { | |
| 1037 | + | if (row[depth] !== undefined && (this.inlineRoot || !item.root)) { | |
| 1038 | + | const start = row[depth]; | |
| 1039 | + | let end = i + (item.bot && depth === level ? 0.5 : 1); | |
| 1040 | + | if (item_y >= lib.panel.filter.y) end -= 1; | |
| 1041 | + | const l_x = Math.round(lib.ui.x + this.treeIndent * depth + lib.ui.sz.margin) + Math.floor(lib.ui.sz.node / 2) - lib.ui.l.wf; | |
| 1042 | + | let l_y = Math.round(lib.ui.y + lib.ui.row.h * start + lib.panel.search.h - lib.sbar.delta); | |
| 1043 | + | let l_h = Math.ceil(lib.ui.row.h * (end - start)) + lib.ui.l.wc; | |
| 1044 | + | if (!start) { | |
| 1045 | + | l_y += lib.ui.row.h / 2; | |
| 1046 | + | l_h -= lib.ui.row.h / 2; | |
| 1047 | + | } | |
| 1048 | + | if (i <= this.row.lineMax[depth] && (!this.inlineRoot || item.level) && grSet.libraryDesign === 'traditional') gr.FillSolidRect(l_x, l_y, lib.ui.l.w, l_h, lib.ui.col.line); | |
| 1049 | + | } | |
| 1050 | + | } | |
| 1051 | + | if (item.bot) row[level] = undefined; | |
| 1052 | + | } | |
| 1053 | + | } | |
| 1054 | + | } | |
| 1055 | + | } | |
| 1056 | + | ||
| 1057 | + | for (i = b; i < f; i++) { | |
| 1058 | + | const item = this.tree[i]; | |
| 1059 | + | const level = !this.inlineRoot ? item.level : Math.max(item.level - 1, 0); | |
| 1060 | + | item_y = Math.round(lib.ui.y + lib.ui.row.h * i + lib.panel.search.h - lib.sbar.delta); | |
| 1061 | + | if (item_y < lib.panel.filter.y) { | |
| 1062 | + | item_x = Math.round(lib.ui.x + this.treeIndent * level + lib.ui.sz.margin); | |
| 1063 | + | if (this.inlineRoot && !item.level) item_x = lib.ui.x + lib.ui.sz.marginSearch; | |
| 1064 | + | if (lib.ui.style.squareNode) { | |
| 1065 | + | if (!item.track && (!this.inlineRoot || item.level)) { | |
| 1066 | + | const y2 = lib.ui.y + lib.ui.row.h * i + y1 - lib.ui.l.wf; | |
| 1067 | + | if (lib.ui.col.line) if (grSet.libraryDesign !== 'reborn') gr.FillSolidRect(item_x + lib.ui.sz.node, y2, lib.ui.l.s1, lib.ui.l.w, lib.ui.col.line); | |
| 1068 | + | this.drawNode(gr, item.child.length < 1 ? this.m.br != i ? 0 : 2 : this.m.br != i ? 1 : 3, item_x, item_y + lib.panel.node_y); | |
| 1069 | + | } else if (lib.ui.col.line && (!this.inlineRoot || item.level)) { | |
| 1070 | + | if (grSet.libraryDesign !== 'reborn') { | |
| 1071 | + | const y2 = Math.round(lib.ui.y + lib.panel.search.h - lib.sbar.delta) + Math.ceil(lib.ui.row.h * (i + 0.5)) - lib.ui.l.wf; | |
| 1072 | + | gr.FillSolidRect(item_x + lib.ui.l.s2, y2, lib.ui.l.s3, lib.ui.l.w, lib.ui.col.line); | |
| 1073 | + | } | |
| 1074 | + | } | |
| 1075 | + | } else if (!item.track && (!this.inlineRoot || item.level)) this.drawNode(gr, item, item_x, item_y, item.child.length < 1, this.m.br == i, item.sel); | |
| 1076 | + | if (!this.inlineRoot || item.level) item_x += lib.ui.icon.w + (!this.fullLineSelection ? lib.ui.l.wf : 0); | |
| 1077 | + | const w = lib.ui.x + lib.panel.tree.w - item_x - lib.ui.sz.sel - item.count_w; | |
| 1078 | + | this.checkTooltip(item, item_x, item_y, item.name_w, w); | |
| 1079 | + | if (this.fullLineSelection && item.id != this.id) { | |
| 1080 | + | item.w = lib.ui.x + lib.panel.tree.w - item_x - HD_4K(25, 45); | |
| 1081 | + | item.id = this.id; | |
| 1082 | + | } | |
| 1083 | + | if (item.np && this.highlight.nowPlayingSidemarker) { | |
| 1084 | + | gr.FillSolidRect(lib.ui.l.w, item_y, lib.ui.sz.sideMarker, lib.ui.row.h, lib.ui.col.nowp); | |
| 1085 | + | } | |
| 1086 | + | if (item.np && this.highlight.nowPlayingIndicator && item.track) nm[i] = item.np + nm[i]; | |
| 1087 | + | const np = item.np && this.highlight.nowPlaying; | |
| 1088 | + | // const txt_co = np && !item.sel ? lib.ui.col.nowp : item.sel && this.fullLineSelection ? (this.highlight.row ? lib.ui.col.textSel : lib.ui.col.text) : this.m.i == i && this.highlight.text ? lib.ui.col.text_h : lib.ui.col.counts || lib.ui.col.count; | |
| 1089 | + | const type = item.sel ? (this.highlight.row || !this.fullLineSelection ? 2 : 0) : this.m.i == i && this.highlight.text ? 1 : 0; | |
| 1090 | + | const txt_c = // np && !item.sel ? lib.ui.col.nowp : lib.ui.col.txtArr[type]; | |
| 1091 | + | this.highlight.nowPlaying && !item.root && this.inRange(this.nowp, item.item) && updatedNowpBg ? lib.ui.col.text_nowp : | |
| 1092 | + | item.sel ? lib.ui.col.textSel : | |
| 1093 | + | this.m.i === i ? lib.ui.col.text_h : lib.ui.col.text; | |
| 1094 | + | ||
| 1095 | + | !lib.panel.colMarker ? gr.GdiDrawText(nm[i], lib.ui.font.main, txt_c, item_x, item_y, w, lib.ui.row.h, lib.panel.lc) : this.cusCol(gr, nm[i], item, item_x, item_y, w, lib.ui.row.h, type, np, lib.ui.font.main, lib.ui.font.mainEllipsisSpace, 'text'); | |
| 1096 | + | if (this.countsRight || this.statisticsShow) { | |
| 1097 | + | const scrollbar = libSet.sbarShow !== 0 && lib.sbar.w === SCALE(12) && lib.sbar.scrollable_lines > 0; | |
| 1098 | + | const x = lib.panel.tree.w - item_x + (grSet.libraryLayout === 'split' ? 0 : lib.ui.x) - (scrollbar ? SCALE(24) : 0); | |
| 1099 | + | gr.GdiDrawText(!this.statisticsShow ? item.count : item.statistics, !item.root || !this.label ? lib.ui.font.small : lib.ui.font.label, txt_c, item_x, item_y, x, lib.ui.row.h, lib.panel.rc); | |
| 1100 | + | } | |
| 1101 | + | } | |
| 1102 | + | } | |
| 1103 | + | } | |
| 1104 | + | ||
| 1105 | + | drawNode(gr, item, x, y, parent, hover, sel) { | |
| 1106 | + | const selCol = sel && this.fullLineSelection && this.highlight.row; | |
| 1107 | + | const y2 = Math.round(y); | |
| 1108 | + | const ix = this.get_ix(x, y, true, false); | |
| 1109 | + | if (ix >= this.tree.length || ix < 0) return; | |
| 1110 | + | const itemtr = this.tree[ix]; | |
| 1111 | + | const nowp = this.highlight.nowPlaying && !itemtr.root && this.inRange(this.nowp, itemtr.item); | |
| 1112 | + | const icon_c = nowp ? lib.ui.col.text_nowp : hover ? lib.ui.col.iconPlus_h : selCol ? lib.ui.col.iconPlus_sel : lib.ui.col.iconPlus; | |
| 1113 | + | ||
| 1114 | + | switch (this.nodeStyle) { | |
| 1115 | + | case 0: // * Squares - Traditional design | |
| 1116 | + | if (!this.highlight.node && item > 1) item -= 2; | |
| 1117 | + | x = Math.round(x); | |
| 1118 | + | y = Math.round(y); | |
| 1119 | + | if (this.nd[item]) gr.DrawImage(this.nd[item], x, y, this.nd[item].Width, this.nd[item].Height, 0, 0, this.nd[item].Width, this.nd[item].Height); | |
| 1120 | + | break; | |
| 1121 | + | case 1: | |
| 1122 | + | case 2: // * Angles/Arrows - Modern design | |
| 1123 | + | if (!this.highlight.row && this.fullLineSelection) x += lib.ui.l.w; | |
| 1124 | + | if (parent) { | |
| 1125 | + | if (hover) { | |
| 1126 | + | gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1127 | + | gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x + 1, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1128 | + | } else { | |
| 1129 | + | gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1130 | + | if (this.nodeStyle === 1) gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x + 1, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1131 | + | } | |
| 1132 | + | } else { | |
| 1133 | + | gr.DrawString(lib.ui.icon.collapse, lib.ui.icon.font, icon_c, x - lib.ui.icon.offset, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1134 | + | gr.DrawString(lib.ui.icon.collapse, lib.ui.icon.font, icon_c, x - lib.ui.icon.offset, y2 + 1, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1135 | + | } | |
| 1136 | + | break; | |
| 1137 | + | case 3: case 4: { // * Triangle - Ultra-modern design | |
| 1138 | + | if (!this.highlight.row && this.fullLineSelection) x += lib.ui.l.w; | |
| 1139 | + | // const y3 = Math.round(y + (lib.ui.row.h - this.sy_sz) / 2 - 2); | |
| 1140 | + | gr.SetSmoothingMode(4); | |
| 1141 | + | if (parent) { | |
| 1142 | + | if (hover) { | |
| 1143 | + | gr.DrawString(lib.ui.icon.expand2, lib.ui.icon.font, icon_c, x, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1144 | + | } else { | |
| 1145 | + | gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1146 | + | if (this.nodeStyle === 3) gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x + 1, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1147 | + | } | |
| 1148 | + | } else { | |
| 1149 | + | gr.DrawString(lib.ui.icon.expand2, lib.ui.icon.font, icon_c, x, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1150 | + | } | |
| 1151 | + | gr.SetSmoothingMode(0); | |
| 1152 | + | break; | |
| 1153 | + | } | |
| 1154 | + | case 5: // * Custom node - Georgia-ReBORN design ( Clean +|- ) | |
| 1155 | + | if (parent) { // Plus | |
| 1156 | + | if (hover) { | |
| 1157 | + | gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x, y2 - HD_4K(1, -1), lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1158 | + | gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x, y2 - HD_4K(1, -1), lib.ui.x + lib.panel.tree.w - x + 2, lib.ui.row.h + 2, lib.panel.s_lc); | |
| 1159 | + | } else gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x, y2 - (DetectWine() ? HD_4K(0, -1) : HD_4K(1, -1)), lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1160 | + | } else { // Minus | |
| 1161 | + | gr.DrawString(lib.ui.icon.collapse, lib.ui.icon.font, icon_c, x - lib.ui.icon.offset, y2 - (DetectWine() ? HD_4K(1, -1) : HD_4K(1, 0)), lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1162 | + | gr.DrawString(lib.ui.icon.collapse, lib.ui.icon.font, icon_c, x - lib.ui.icon.offset, y2 - HD_4K(1, -1), lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h + 2, lib.panel.s_lc); | |
| 1163 | + | } | |
| 1164 | + | break; | |
| 1165 | + | case 7: | |
| 1166 | + | if (item > 1) item -= 2; | |
| 1167 | + | lib.ui.style.symb.SetPartAndStateID(2, !item ? 1 : 2); | |
| 1168 | + | lib.ui.style.symb.DrawThemeBackground(gr, x, y, lib.ui.sz.node, lib.ui.sz.node); | |
| 1169 | + | break; | |
| 1170 | + | default: | |
| 1171 | + | if (parent) { | |
| 1172 | + | if (hover) { | |
| 1173 | + | gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, selCol ? lib.ui.col.textSel : this.highlight.node ? lib.ui.col.icon_h : lib.ui.col.icon_e, x, y + this.iconVerticalPad, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1174 | + | } else gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, !selCol ? lib.ui.col.icon_c : lib.ui.col.textSel, x, y + this.iconVerticalPad, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1175 | + | } else { | |
| 1176 | + | if (hover) { | |
| 1177 | + | gr.DrawString(lib.ui.icon.collapse, lib.ui.icon.font, selCol ? lib.ui.col.textSel : this.highlight.node ? lib.ui.col.icon_h : lib.ui.col.icon_c, x - lib.ui.icon.offset, y + this.iconVerticalPad, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1178 | + | } else gr.DrawString(lib.ui.icon.collapse, lib.ui.icon.font, !selCol ? lib.ui.col.icon_e : lib.ui.col.textSel, x - lib.ui.icon.offset, y + this.iconVerticalPad, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); | |
| 1179 | + | } | |
| 1180 | + | break; | |
| 1181 | + | } | |
| 1182 | + | } | |
| 1183 | + | ||
| 1184 | + | expand(ie, nm) { | |
| 1185 | + | let h = 0; | |
| 1186 | + | let m = 0; | |
| 1187 | + | if (ie) this.tree[ie].sel = true; | |
| 1188 | + | if (!this.tree.some(v => v.sel)) return; | |
| 1189 | + | if (libSet.autoCollapse) { | |
| 1190 | + | const parent = []; | |
| 1191 | + | const pr_pr = []; | |
| 1192 | + | let par = 0; | |
| 1193 | + | this.tree.forEach((v, j, arr) => { | |
| 1194 | + | if (v.sel) { | |
| 1195 | + | j = v.level; | |
| 1196 | + | if (this.rootNode) j -= 1; | |
| 1197 | + | if (v.level != 0) { | |
| 1198 | + | par = v.par; | |
| 1199 | + | for (m = 1; m < j + 1; m++) { | |
| 1200 | + | pr_pr[m] = m == 1 ? par : arr[pr_pr[m - 1]].par; | |
| 1201 | + | parent.push(pr_pr[m]); | |
| 1202 | + | } | |
| 1203 | + | } | |
| 1204 | + | } | |
| 1205 | + | }); | |
| 1206 | + | this.tree.forEach((v, i) => { | |
| 1207 | + | if (!parent.includes(i) && !v.sel && !v.root) v.child = []; | |
| 1208 | + | }); | |
| 1209 | + | this.buildTree(lib.lib.root, 0); | |
| 1210 | + | } | |
| 1211 | + | const start_l = this.tree.length; | |
| 1212 | + | let nm_n = ''; | |
| 1213 | + | let nodes = -1; | |
| 1214 | + | m = this.tree.length; | |
| 1215 | + | this.expandedTracks = 0; | |
| 1216 | + | this.expandLmt = lib.men.treeExpandLimit; | |
| 1217 | + | while (m--) { | |
| 1218 | + | if (this.tree[m].sel) { | |
| 1219 | + | this.expandNodes(this.tree[m], !(!this.rootNode || m)); | |
| 1220 | + | nodes++; | |
| 1221 | + | } | |
| 1222 | + | } | |
| 1223 | + | lib.sbar.setRows(this.tree.length); | |
| 1224 | + | lib.panel.treePaint(); | |
| 1225 | + | if (nm) { | |
| 1226 | + | this.tree.some((v, i, arr) => { | |
| 1227 | + | nm_n = (v.level ? arr[v.par].srt[0] : '') + v.srt[0]; | |
| 1228 | + | nm_n = nm_n.toUpperCase(); | |
| 1229 | + | if (nm_n == nm) { | |
| 1230 | + | h = i; | |
| 1231 | + | return true; | |
| 1232 | + | } | |
| 1233 | + | }); | |
| 1234 | + | } else { | |
| 1235 | + | this.tree.some((v, i) => { | |
| 1236 | + | if (v.sel) { | |
| 1237 | + | h = i; | |
| 1238 | + | return true; | |
| 1239 | + | } | |
| 1240 | + | }); | |
| 1241 | + | } | |
| 1242 | + | const new_items = this.tree.length - start_l + nodes; | |
| 1243 | + | const b = Math.round(lib.sbar.scroll / lib.ui.row.h + 0.4); | |
| 1244 | + | const n = Math.max(h - b, this.rootNode ? 1 : 0); | |
| 1245 | + | let scrollChk = false; | |
| 1246 | + | if (n + 1 + new_items > this.rows) { | |
| 1247 | + | scrollChk = true; | |
| 1248 | + | if (new_items > this.rows - 2) lib.sbar.checkScroll(h * lib.ui.row.h); | |
| 1249 | + | else lib.sbar.checkScroll(Math.min(h * lib.ui.row.h, (h + 1 - lib.sbar.rows_drawn + new_items) * lib.ui.row.h)); | |
| 1250 | + | } | |
| 1251 | + | if (lib.sbar.scroll > h * lib.ui.row.h) { | |
| 1252 | + | scrollChk = true; | |
| 1253 | + | lib.sbar.checkScroll(h * lib.ui.row.h); | |
| 1254 | + | } | |
| 1255 | + | if (!scrollChk) lib.sbar.scrollRound(); | |
| 1256 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 1257 | + | } | |
| 1258 | + | ||
| 1259 | + | expandCollapse(x, y, item, ix) { | |
| 1260 | + | const expanded = item.child.length > 0 ? 1 : 0; | |
| 1261 | + | switch (expanded) { | |
| 1262 | + | case 0: { | |
| 1263 | + | let n = 0; | |
| 1264 | + | if (libSet.autoCollapse) { | |
| 1265 | + | n = this.branchChange(item, false, true); | |
| 1266 | + | lib.sbar.checkScroll(lib.sbar.scroll - n * lib.ui.row.h, 'step'); | |
| 1267 | + | } | |
| 1268 | + | const row = this.getRowNumber(y); | |
| 1269 | + | this.branch(item, !!item.root, true); | |
| 1270 | + | if (!ix) lib.panel.setHeight(true); | |
| 1271 | + | n = 2; | |
| 1272 | + | if (item.child.length == 1 && libSet.treeAutoExpandSingle) { | |
| 1273 | + | this.branch(item.child[0], false, true); | |
| 1274 | + | n += item.child[0].child.length; | |
| 1275 | + | } | |
| 1276 | + | if (libSet.autoCollapse) ix = item.ix; | |
| 1277 | + | if (row + n + item.child.length > this.rows) { | |
| 1278 | + | if (item.child.length > (this.rows - n)) lib.sbar.checkScroll(ix * lib.ui.row.h); | |
| 1279 | + | else lib.sbar.checkScroll(Math.min(ix * lib.ui.row.h, (ix + n - lib.sbar.rows_drawn + item.child.length) * lib.ui.row.h)); | |
| 1280 | + | } | |
| 1281 | + | break; | |
| 1282 | + | } | |
| 1283 | + | case 1: { | |
| 1284 | + | this.clearChild(item); | |
| 1285 | + | if (!ix && this.tree.length == 1) lib.panel.setHeight(false); | |
| 1286 | + | const b = $Lib.clamp(Math.round(lib.sbar.delta / lib.ui.row.h + 0.4), 0, this.tree.length - 1); | |
| 1287 | + | const f = Math.min(b + lib.panel.rows, this.tree.length); | |
| 1288 | + | if (f - b < lib.panel.rows) lib.sbar.checkScroll((this.tree.length - lib.panel.rows) * lib.ui.row.h); | |
| 1289 | + | break; | |
| 1290 | + | } | |
| 1291 | + | } | |
| 1292 | + | if (lib.sbar.scroll > ix * lib.ui.row.h) lib.sbar.checkScroll(ix * lib.ui.row.h); | |
| 1293 | + | } | |
| 1294 | + | ||
| 1295 | + | expandNodes(obj, am) { | |
| 1296 | + | this.branch(obj, !!am, true, true); | |
| 1297 | + | if (obj.child) { | |
| 1298 | + | obj.child.some(v => { | |
| 1299 | + | if (v.track) this.expandedTracks++; | |
| 1300 | + | if (this.expandedTracks >= this.expandLmt) return true; | |
| 1301 | + | if (!v.track) this.expandNodes(v); | |
| 1302 | + | }); | |
| 1303 | + | } | |
| 1304 | + | } | |
| 1305 | + | ||
| 1306 | + | fixMarkers(n) { | |
| 1307 | + | while (n.includes('@!##!#')) { // $colour | |
| 1308 | + | n = n.replace('@!##!#', '<!>'); | |
| 1309 | + | n = n.replace('@!#', '~#~'); | |
| 1310 | + | } | |
| 1311 | + | n = n.replace(Regex.LibMarkerExcl, '@!##!#').replace(Regex.LibMarkerTildeHash, '#!#@!#'); | |
| 1312 | + | ||
| 1313 | + | while (n.includes('#@##!#')) { // $nodisplay | |
| 1314 | + | n = n.replace('#@##!#', '<!>'); | |
| 1315 | + | n = n.replace('#@#', '~#~'); | |
| 1316 | + | } | |
| 1317 | + | n = n.replace(Regex.LibMarkerExcl, '#@##!#').replace(Regex.LibMarkerTildeHash, '#!##@#'); | |
| 1318 | + | return n; | |
| 1319 | + | } | |
| 1320 | + | ||
| 1321 | + | focusShow(i) { | |
| 1322 | + | this.setTreeSel(i); | |
| 1323 | + | lib.panel.treePaint(); | |
| 1324 | + | this.showItem(i); | |
| 1325 | + | } | |
| 1326 | + | ||
| 1327 | + | formatBytes(bytes, decimals = 1) { | |
| 1328 | + | if (!+bytes) return '0 字节' | |
| 1329 | + | ||
| 1330 | + | const k = 1024 | |
| 1331 | + | const dm = decimals < 0 ? 0 : decimals | |
| 1332 | + | const sizes = ['字节', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] | |
| 1333 | + | ||
| 1334 | + | const i = Math.floor(Math.log(bytes) / Math.log(k)) | |
| 1335 | + | ||
| 1336 | + | return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}` | |
| 1337 | + | } | |
| 1338 | + | ||
| 1339 | + | getAllCombinations(n) { | |
| 1340 | + | n = this.fixMarkers(n); | |
| 1341 | + | return n.includes('^@^') && n.includes(lib.panel.softSplitter) ? this.imgView(n) : this.getCombos(n); | |
| 1342 | + | } | |
| 1343 | + | ||
| 1344 | + | getCombos(n) { | |
| 1345 | + | const combinations = []; | |
| 1346 | + | const divisors = []; | |
| 1347 | + | const arraysToCombine = []; | |
| 1348 | + | n = n.replace(RegExp(`(#!#|)${lib.panel.softSplitter}(#!#|)`, 'g'), '@@').split('#!#'); | |
| 1349 | + | const ln = n.length; | |
| 1350 | + | let i = 0; | |
| 1351 | + | for (i = 0; i < ln; i++) { | |
| 1352 | + | n[i] = n[i].split('@@'); | |
| 1353 | + | if (n[i] != '') arraysToCombine.push(n[i]); | |
| 1354 | + | } | |
| 1355 | + | const arraysToCombineLength = arraysToCombine.length; | |
| 1356 | + | for (i = arraysToCombineLength - 1; i >= 0; i--) divisors[i] = divisors[i + 1] ? divisors[i + 1] * arraysToCombine[i + 1].length : 1; | |
| 1357 | + | const getPermutation = (n, arraysToCombine) => { | |
| 1358 | + | const result = []; | |
| 1359 | + | let cur_array; | |
| 1360 | + | for (let j = 0; j < arraysToCombineLength; j++) { | |
| 1361 | + | cur_array = arraysToCombine[j]; | |
| 1362 | + | result.push(cur_array[Math.floor(n / divisors[j]) % cur_array.length]); | |
| 1363 | + | } | |
| 1364 | + | return result; | |
| 1365 | + | }; | |
| 1366 | + | let numPerms = arraysToCombine[0].length; | |
| 1367 | + | for (i = 1; i < arraysToCombineLength; i++) numPerms *= arraysToCombine[i].length; | |
| 1368 | + | for (i = 0; i < numPerms; i++) combinations.push(getPermutation(i, arraysToCombine)); | |
| 1369 | + | return this.removeDuplicateArr(combinations); | |
| 1370 | + | } | |
| 1371 | + | ||
| 1372 | + | getChildCount(arr, ix) { | |
| 1373 | + | arr.forEach(v => { | |
| 1374 | + | if (v.child && ix > v.ix) { | |
| 1375 | + | this.childCount += v.child.length; | |
| 1376 | + | if (!v.track) this.getChildCount(v.child, ix); | |
| 1377 | + | } | |
| 1378 | + | }); | |
| 1379 | + | } | |
| 1380 | + | ||
| 1381 | + | getItemCount(v) { | |
| 1382 | + | const prop = !lib.panel.imgView ? 'statistics' : '_statistics'; | |
| 1383 | + | if (this.statisticsShow && v[prop] == null) v[prop] = v.root && this.label && !lib.panel.imgView ? this.label : this.calcStatistics(v); | |
| 1384 | + | if (v.count === '' && this.nodeCounts) { | |
| 1385 | + | if (!lib.panel.imgView) { | |
| 1386 | + | if (v.root && this.label) { | |
| 1387 | + | if (!this.statisticsShow) v.count = this.label; | |
| 1388 | + | } else { | |
| 1389 | + | const type = lib.panel.search.txt ? 'search' : libSet.filterBy ? 'filter' : 'standard'; | |
| 1390 | + | const key = this.getKey(v); | |
| 1391 | + | v.count = !v.track || !this.showTracks ? (v.name ? ' ' : '') + (this.nodeCounts == 1 ? `(${this.trackCount(v.item)})` : this.nodeCounts == 2 ? `(${this.branchCount(v, !!v.root, true, false, key, type)})` : '') : ''; | |
| 1392 | + | if (!this.showTracks && v.count == `${v.name ? ' ' : ''}(0)`) v.count = ''; | |
| 1393 | + | if (this.countsRight && !this.statisticsShow) v.count = v.count.replace(Regex.PunctParen, ''); | |
| 1394 | + | } | |
| 1395 | + | } else { | |
| 1396 | + | const getTracks = [true, true, true, true, false, false, true, false, false, false, false][libSet.itemShowStatistics]; | |
| 1397 | + | if (getTracks) { | |
| 1398 | + | v.count = this.trackCount(v.item); | |
| 1399 | + | v.count += v.count > 1 ? ' 首' : ' 首'; | |
| 1400 | + | } | |
| 1401 | + | const getItemCount = !v.root && libSet.itemOverlayType != 1 && libSet.albumArtLabelType == 2 && !libSet.itemShowStatistics && (lib.pop.nodeCounts == 1 || lib.pop.nodeCounts == 2); | |
| 1402 | + | if (getItemCount) { | |
| 1403 | + | const count = v.count.replace(Regex.NumNonDigits, ''); | |
| 1404 | + | if (lib.panel.lines == 1 || libSet.albumArtFlipLabels) v.grp += ` (${count})`; | |
| 1405 | + | else v.lot += ` (${count})`; | |
| 1406 | + | } | |
| 1407 | + | } | |
| 1408 | + | } | |
| 1409 | + | } | |
| 1410 | + | ||
| 1411 | + | getHandleList(n) { | |
| 1412 | + | if (n == 'newItems') this.getTreeSel(); | |
| 1413 | + | const handleList = new FbMetadbHandleList(); | |
| 1414 | + | this.sel_items.some(v => { | |
| 1415 | + | if (v >= lib.panel.list.Count) return true; | |
| 1416 | + | handleList.Add(lib.panel.list[v]); | |
| 1417 | + | }); | |
| 1418 | + | return handleList; | |
| 1419 | + | } | |
| 1420 | + | ||
| 1421 | + | get_ix(x, y, simple, type) { | |
| 1422 | + | let ix; | |
| 1423 | + | y -= lib.ui.y - 1; // - 1 = workaround to adjust and fix background color selection ( text_nowp in drawNode() ) to draw correct colored nodes in tree when option "Nowplaying in highlight" is active | |
| 1424 | + | x -= lib.ui.x; | |
| 1425 | + | if (lib.panel.imgView) { | |
| 1426 | + | if (y > libImg.panel.y && y < libImg.panel.y + libImg.panel.h && x > libImg.panel.x && x < libImg.panel.x + libImg.panel.w) { | |
| 1427 | + | const row_ix = libImg.style.vertical ? Math.ceil((y + lib.sbar.delta - libImg.panel.y) / libImg.row.h) - 1 : 0; | |
| 1428 | + | const column_ix = libImg.style.vertical ? (!libImg.labels.right && !libSet.albumArtFlowMode ? Math.ceil((x - libImg.panel.x) / libImg.columnWidth) - 1 : 0) : Math.ceil((x + lib.sbar.delta - libImg.panel.x) / libImg.columnWidth) - 1; | |
| 1429 | + | ix = (row_ix * libImg.columns) + column_ix; | |
| 1430 | + | return ix > this.tree.length - 1 ? -1 : ix; | |
| 1431 | + | } | |
| 1432 | + | return -1; | |
| 1433 | + | } | |
| 1434 | + | ix = y > lib.panel.tree.y && y < lib.panel.tree.y + this.rows * lib.ui.row.h ? Math.round((y + lib.sbar.delta - lib.panel.search.h - lib.ui.row.h * 0.5) / lib.ui.row.h) : -1; | |
| 1435 | + | if (simple) return ix; | |
| 1436 | + | return this.tree.length > ix && ix >= 0 && x < lib.panel.tree.w && y > lib.panel.tree.y && y < lib.panel.tree.y + this.rows * lib.ui.row.h && this.check_ix(this.tree[ix], x + lib.ui.x, y + lib.ui.y, type) ? ix : -1; | |
| 1437 | + | } | |
| 1438 | + | ||
| 1439 | + | getKey(v) { | |
| 1440 | + | const level = v.level; | |
| 1441 | + | const o = { | |
| 1442 | + | a: v.nm || '', | |
| 1443 | + | b: level != 0 ? this.tree[v.par].nm : '', | |
| 1444 | + | c: level > 1 ? this.tree[this.tree[v.par].par].nm : '', | |
| 1445 | + | d: level > 2 ? this.tree[this.tree[this.tree[v.par].par].par].nm : '' | |
| 1446 | + | } | |
| 1447 | + | return level + (level > 2 ? o.d : '') + (level > 1 ? o.c : '') + (level > 0 ? o.b : '') + o.a; | |
| 1448 | + | } | |
| 1449 | + | ||
| 1450 | + | getNowplaying(handle, stop) { | |
| 1451 | + | if (stop) { | |
| 1452 | + | lib.panel.treePaint(); | |
| 1453 | + | return this.nowp = -1; | |
| 1454 | + | } | |
| 1455 | + | if (!handle && fb.IsPlaying) handle = fb.GetNowPlaying(); | |
| 1456 | + | if (!handle) return this.nowp = -1; | |
| 1457 | + | this.nowp = lib.panel.list.Find(handle); | |
| 1458 | + | lib.panel.treePaint(); | |
| 1459 | + | } | |
| 1460 | + | ||
| 1461 | + | getNumbers(arr) { // test [0, '0', "0", "0.5", 10, '10', "", '', '-', null, true, false, 'Oh'] | |
| 1462 | + | return arr.filter(v => Number(v)); // gives ["0.5", 10, "10", true] | |
| 1463 | + | //return arr.filter(v => parseFloat(v) == v); // gives [0, "0", "0", "0.5", 10, "10"] | |
| 1464 | + | //return arr.filter(v => Number(v) && parseFloat(v) == v); // gives ["0.5", 10, "10"] | |
| 1465 | + | } | |
| 1466 | + | ||
| 1467 | + | getRowNumber(y) { | |
| 1468 | + | return Math.round((y - lib.panel.tree.y - lib.ui.row.h * 0.5) / lib.ui.row.h); | |
| 1469 | + | } | |
| 1470 | + | ||
| 1471 | + | getTreeSel() { | |
| 1472 | + | lib.panel.treePaint(); | |
| 1473 | + | this.sel_items = []; | |
| 1474 | + | this.tree.forEach(v => { | |
| 1475 | + | if (v.sel) this.addItems(this.sel_items, v.item); | |
| 1476 | + | }); | |
| 1477 | + | this.uniq(this.sel_items); | |
| 1478 | + | } | |
| 1479 | + | ||
| 1480 | + | imgView(n) { | |
| 1481 | + | let a; let b; const c = []; | |
| 1482 | + | n = n.split('^@^'); | |
| 1483 | + | if (n[0]) a = this.getCombos(n[0]); | |
| 1484 | + | if (n[1]) b = this.getCombos(n[1]); | |
| 1485 | + | a.forEach(v => b.forEach(w => c.push([`${v.join('')}^@^${w.join('')}`]))); | |
| 1486 | + | return this.removeDuplicateArr(c); | |
| 1487 | + | } | |
| 1488 | + | ||
| 1489 | + | isDate(n) { | |
| 1490 | + | return isNaN(n) && !isNaN(Date.parse(n)); | |
| 1491 | + | } | |
| 1492 | + | ||
| 1493 | + | inRange(num, item) { | |
| 1494 | + | return item.some(v => { | |
| 1495 | + | const end = v.end; | |
| 1496 | + | const start = v.start; | |
| 1497 | + | return num >= Math.min(start, end) && num <= Math.max(start, end); | |
| 1498 | + | }); | |
| 1499 | + | } | |
| 1500 | + | ||
| 1501 | + | lbtn_dblclk(x, y) { | |
| 1502 | + | if (this.autoPlay.click > 2 && libSet.libSource) return; | |
| 1503 | + | if (lib.vk.k('alt')) { | |
| 1504 | + | this.mbtnDblClickOrAltDblClick(x, y, '', 'alt'); | |
| 1505 | + | return; | |
| 1506 | + | } | |
| 1507 | + | this.dbl_clicked = true; | |
| 1508 | + | if (y < lib.panel.search.h) return; | |
| 1509 | + | const ix = this.get_ix(x, y, true, false); | |
| 1510 | + | if (ix >= this.tree.length || ix < 0) return; | |
| 1511 | + | const item = this.tree[ix]; | |
| 1512 | + | switch (this.clicked_on) { | |
| 1513 | + | case 'node': | |
| 1514 | + | this.expandCollapse(x, y, item, ix); | |
| 1515 | + | break; | |
| 1516 | + | case 'text': | |
| 1517 | + | if (!this.check_ix(item, x, y, false)) return; | |
| 1518 | + | if (this.dblClickAction == 3) { | |
| 1519 | + | const handleList = new FbMetadbHandleList(); | |
| 1520 | + | this.range(item.item).forEach(v => { | |
| 1521 | + | if (v < lib.panel.list.Count) handleList.Add(lib.panel.list[v]); | |
| 1522 | + | }); | |
| 1523 | + | if (handleList.Count) plman.FlushPlaybackQueue(); | |
| 1524 | + | for (let i = 0; i < handleList.Count; i++) { | |
| 1525 | + | plman.AddItemToPlaybackQueue(handleList[i]); | |
| 1526 | + | } | |
| 1527 | + | fb.Play(); | |
| 1528 | + | return; | |
| 1529 | + | } | |
| 1530 | + | if (!libSet.libSource) { | |
| 1531 | + | plman.ExecutePlaylistDefaultAction($Lib.pl_active, this.range(item.item)[0]); | |
| 1532 | + | //plman.ExecutePlaylistDefaultAction($.pl_active, plman.GetPlaylistFocusItemIndex($.pl_active));//Asion 修正来源选择列表模式下搜索内容播放不正常 | |
| 1533 | + | ||
| 1534 | + | /*const plsIdx = libSet.fixedPlaylist ? plman.FindPlaylist(libSet.fixedPlaylistName) : $.pl_active; | |
| 1535 | + | if (plsIdx !== -1) { | |
| 1536 | + | const idx = lib.panel.search.txt.length | |
| 1537 | + | ? plman.GetPlaylistItems(plsIdx).Find(lib.panel.list[this.range(item.item)[0]]) | |
| 1538 | + | : this.range(item.item)[0]; | |
| 1539 | + | if (idx !== -1) { plman.ExecutePlaylistDefaultAction(plsIdx, idx); } | |
| 1540 | + | } */ | |
| 1541 | + | return; | |
| 1542 | + | } | |
| 1543 | + | if (!this.dblClickAction && !this.autoFill.mouse && !this.autoPlay.click) return this.send(item, x, y); | |
| 1544 | + | if (this.dblClickAction === 2 && !item.track && !lib.panel.imgView) { | |
| 1545 | + | this.expandCollapse(x, y, item, ix); | |
| 1546 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 1547 | + | } | |
| 1548 | + | if (!this.dblClickAction || this.autoPlay.click === 2) return; | |
| 1549 | + | if (this.dblClickAction !== 2 && this.dblClickAction !== 3 || this.dblClickAction === 2 && item.track || this.dblClickAction === 2 && lib.panel.imgView) { | |
| 1550 | + | if (!this.autoFill.mouse) this.send(item, x, y); | |
| 1551 | + | let pl_stnd_idx = plman.FindOrCreatePlaylist(libSet.libPlaylist.replace(Regex.TFLibViewName, lib.panel.viewName), false); | |
| 1552 | + | if (libSet.sendToCur) pl_stnd_idx = plman.ActivePlaylist; | |
| 1553 | + | else plman.ActivePlaylist = pl_stnd_idx; | |
| 1554 | + | plman.ActivePlaylist = pl_stnd_idx; | |
| 1555 | + | const c = (plman.PlaybackOrder === 3 || plman.PlaybackOrder === 4) ? Math.ceil(plman.PlaylistItemCount(pl_stnd_idx) * Math.random() - 1) : 0; | |
| 1556 | + | plman.ExecutePlaylistDefaultAction(pl_stnd_idx, c); | |
| 1557 | + | } | |
| 1558 | + | if (libSet.dblClickAction === 3) { | |
| 1559 | + | if (plman.PlaylistItemCount(plman.ActivePlaylist) <= 0) { | |
| 1560 | + | lib.panel.pos = 0; | |
| 1561 | + | this.setTreeSel(this.tree[lib.panel.pos].ix); | |
| 1562 | + | this.setPlaylist(lib.panel.pos, this.tree[lib.panel.pos]); | |
| 1563 | + | this.load(lib.panel.pos, true, true, true, false, false); | |
| 1564 | + | } | |
| 1565 | + | plman.ExecutePlaylistDefaultAction($Lib.pl_active, this.sel_items[0]); | |
| 1566 | + | this.load(this.sel_items, true, false, true, false, false); | |
| 1567 | + | if (!item.root) { | |
| 1568 | + | setTimeout(() => { fb.RunMainMenuCommand('编辑/撤消'); }, 100); | |
| 1569 | + | } | |
| 1570 | + | } | |
| 1571 | + | break; | |
| 1572 | + | } | |
| 1573 | + | } | |
| 1574 | + | ||
| 1575 | + | lbtn_dn(x, y) { | |
| 1576 | + | this.lbtnDn = false; | |
| 1577 | + | this.dbl_clicked = false; | |
| 1578 | + | if (y < lib.panel.search.h) return; | |
| 1579 | + | const ix = this.get_ix(x, y, true, false); | |
| 1580 | + | if (ix >= this.tree.length || ix < 0) return; | |
| 1581 | + | this.deactivateTooltip(); | |
| 1582 | + | if (libSet.touchControl) { | |
| 1583 | + | lib.ui.id.dragDrop = lib.ui.id.touch_dn = ix; | |
| 1584 | + | } | |
| 1585 | + | const item = this.tree[ix]; | |
| 1586 | + | this.clicked_on = this.clickedOn(x, y, item); | |
| 1587 | + | switch (this.clicked_on) { | |
| 1588 | + | case 'node': | |
| 1589 | + | lib.panel.pos = ix; | |
| 1590 | + | this.expandCollapse(x, y, item, ix); | |
| 1591 | + | this.checkRow(x, y); | |
| 1592 | + | break; | |
| 1593 | + | case 'text': | |
| 1594 | + | this.last_pressed_coord.x = x - lib.ui.x; | |
| 1595 | + | this.last_pressed_coord.y = y - lib.ui.y; | |
| 1596 | + | this.lbtnDn = true; | |
| 1597 | + | lib.panel.pos = ix; | |
| 1598 | + | if (libSet.touchControl) break; | |
| 1599 | + | if (lib.vk.k('alt')) { | |
| 1600 | + | this.alt_dbl_clicked = false; | |
| 1601 | + | if (libSet.altClickAction == 2) { | |
| 1602 | + | return; | |
| 1603 | + | } | |
| 1604 | + | if (this.autoFill.mouse) return; | |
| 1605 | + | } | |
| 1606 | + | if (!item.sel && !lib.vk.k('ctrl')) this.setTreeSel(ix, item.sel); | |
| 1607 | + | break; | |
| 1608 | + | } | |
| 1609 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 1610 | + | } | |
| 1611 | + | ||
| 1612 | + | lbtn_up(x, y) { | |
| 1613 | + | if (lib.lib.empty && libSet.libSource == 1 && !libSet.fixedPlaylist && y > lib.panel.search.h) fb.RunMainMenuCommand('媒体库/配置'); | |
| 1614 | + | this.last_pressed_coord = { | |
| 1615 | + | x: undefined, | |
| 1616 | + | y: undefined | |
| 1617 | + | }; | |
| 1618 | + | this.lbtnDn = false; | |
| 1619 | + | if (y < lib.panel.search.h || x < lib.ui.x || this.dbl_clicked || lib.but.Dn) return; | |
| 1620 | + | const ix = this.get_ix(x, y, true, false); | |
| 1621 | + | lib.panel.pos = ix; | |
| 1622 | + | if (ix >= this.tree.length || ix < 0) return; | |
| 1623 | + | if (libSet.touchControl && (this.autoFill.mouse || this.autoPlay.click) && lib.ui.id.touch_dn != ix) return; | |
| 1624 | + | const item = this.tree[ix]; | |
| 1625 | + | if (this.clicked_on != 'text') return; | |
| 1626 | + | if (!libSet.libSource) return this.setPlaylistSelection(ix, item); | |
| 1627 | + | if (lib.vk.k('alt')) { | |
| 1628 | + | if (grSet.addTracksPlaylistSwitch) { | |
| 1629 | + | grm.button.btn.library.enabled = false; | |
| 1630 | + | grm.button.btn.library.changeButtonState(ButtonState.Default); | |
| 1631 | + | grm.ui.displayLibrary = false; | |
| 1632 | + | grm.ui.displayPlaylist = true; | |
| 1633 | + | if (!grSet.playlistAutoScrollNowPlaying) grm.ui.setPlaylistSize(); | |
| 1634 | + | setTimeout(() => { | |
| 1635 | + | if (pl.playlist.is_scrollbar_available) { | |
| 1636 | + | pl.playlist.scrollbar.scroll_to_end(); | |
| 1637 | + | } | |
| 1638 | + | }, 500); | |
| 1639 | + | window.Repaint(); | |
| 1640 | + | } | |
| 1641 | + | this.mbtnUpOrAltClickUp(x, y, '', 'alt'); // { | |
| 1642 | + | return; | |
| 1643 | + | } | |
| 1644 | + | if (!lib.vk.k('ctrl')) { | |
| 1645 | + | this.clearSelected(); | |
| 1646 | + | if (!item.sel) this.setTreeSel(ix, item.sel); | |
| 1647 | + | } else this.setTreeSel(ix, item.sel); | |
| 1648 | + | if (this.autoFill.mouse || this.autoPlay.click) { | |
| 1649 | + | window.Repaint(true); | |
| 1650 | + | this.send(item, x, y); | |
| 1651 | + | } else { | |
| 1652 | + | lib.panel.treePaint(); | |
| 1653 | + | } | |
| 1654 | + | this.track(this.autoFill.mouse || this.autoPlay.click); | |
| 1655 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 1656 | + | } | |
| 1657 | + | ||
| 1658 | + | leave() { | |
| 1659 | + | this.deactivateTooltip(); | |
| 1660 | + | lib.panel.m.x = -1; | |
| 1661 | + | lib.panel.m.y = -1; | |
| 1662 | + | if (lib.men.r_up) return; | |
| 1663 | + | this.m.br = -1; | |
| 1664 | + | this.m.cur_br = 0; | |
| 1665 | + | this.m.i = -1; | |
| 1666 | + | this.cur_ix = 0; | |
| 1667 | + | this.row.i = -1; | |
| 1668 | + | this.row.cur = 0; | |
| 1669 | + | lib.panel.treePaint(); | |
| 1670 | + | } | |
| 1671 | + | ||
| 1672 | + | leftKeyCheckScroll() { | |
| 1673 | + | const row = (lib.panel.pos * lib.ui.row.h - lib.sbar.scroll) / lib.ui.row.h; | |
| 1674 | + | if (lib.sbar.scroll > lib.panel.pos * lib.ui.row.h) lib.sbar.checkScroll(lib.panel.pos * lib.ui.row.h); | |
| 1675 | + | else if (row - lib.sbar.rows_drawn > 0) { | |
| 1676 | + | lib.sbar.checkScroll((lib.panel.pos + 3 - lib.sbar.rows_drawn) * lib.ui.row.h); | |
| 1677 | + | } | |
| 1678 | + | else lib.sbar.scrollRound(); | |
| 1679 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 1680 | + | } | |
| 1681 | + | ||
| 1682 | + | load(list, isArray, add, autoPlay, def_pl, insert) { | |
| 1683 | + | let np_item = -1; | |
| 1684 | + | let pid = -1; | |
| 1685 | + | const pl_stnd = libSet.libPlaylist.replace(Regex.TFLibViewName, lib.panel.viewName); | |
| 1686 | + | let pl_stnd_idx = plman.FindOrCreatePlaylist(pl_stnd, true); | |
| 1687 | + | ||
| 1688 | + | if (!def_pl && plman.ActivePlaylist != -1) pl_stnd_idx = plman.ActivePlaylist; | |
| 1689 | + | else if (libSet.activateOnChange) plman.ActivePlaylist = pl_stnd_idx; | |
| 1690 | + | ||
| 1691 | + | if (autoPlay == 4 && plman.PlaylistItemCount(pl_stnd_idx) || autoPlay == 3 && fb.IsPlaying) { | |
| 1692 | + | autoPlay = false; | |
| 1693 | + | add = true; | |
| 1694 | + | } | |
| 1695 | + | const items = isArray ? this.getHandleList() : list.Clone(); | |
| 1696 | + | ||
| 1697 | + | this.sortIfNeeded(items); | |
| 1698 | + | this.selList = items.Clone(); | |
| 1699 | + | this.selection_holder.SetSelection(this.selList); | |
| 1700 | + | const plnIsValid = pl_stnd_idx != -1 && pl_stnd_idx < plman.PlaylistCount; | |
| 1701 | + | const pllockRemoveOrAdd = plnIsValid ? plman.GetPlaylistLockedActions(pl_stnd_idx).includes('RemoveItems') || plman.GetPlaylistLockedActions(pl_stnd_idx).includes('ReplaceItems') || plman.GetPlaylistLockedActions(pl_stnd_idx).includes('AddItems') : false; | |
| 1702 | + | if (!add && pllockRemoveOrAdd) return; | |
| 1703 | + | if (fb.IsPlaying && !add) { | |
| 1704 | + | if (libSet.actionMode == 1) { | |
| 1705 | + | const pl_playing = `${libSet.libPlaylist} (正在播放)`; | |
| 1706 | + | const pl_playing_idx = plman.FindOrCreatePlaylist(pl_playing, false); | |
| 1707 | + | if (plman.PlayingPlaylist == pl_stnd_idx) { | |
| 1708 | + | plman.RenamePlaylist(pl_stnd_idx, pl_playing); | |
| 1709 | + | plman.RenamePlaylist(pl_playing_idx, pl_stnd); | |
| 1710 | + | plman.SetPlaylistSelection(pl_playing_idx, $Lib.range(0, plman.PlaylistItemCount(pl_playing_idx) - 1), true); | |
| 1711 | + | plman.RemovePlaylistSelection(pl_playing_idx, false); | |
| 1712 | + | plman.InsertPlaylistItems(pl_playing_idx, 0, items, false); | |
| 1713 | + | plman.MovePlaylist(pl_playing_idx, pl_stnd_idx); | |
| 1714 | + | plman.MovePlaylist(pl_stnd_idx + 1, pl_playing_idx); | |
| 1715 | + | } else { | |
| 1716 | + | plman.SetPlaylistSelection(pl_stnd_idx, $Lib.range(0, plman.PlaylistItemCount(pl_stnd_idx) - 1), true); | |
| 1717 | + | plman.RemovePlaylistSelection(pl_stnd_idx, false); | |
| 1718 | + | plman.InsertPlaylistItems(pl_stnd_idx, 0, items, false); | |
| 1719 | + | } | |
| 1720 | + | plman.ActivePlaylist = pl_stnd_idx; | |
| 1721 | + | } else if (fb.GetNowPlaying()) { | |
| 1722 | + | np_item = items.Find(fb.GetNowPlaying()); | |
| 1723 | + | let pl_chk = true; | |
| 1724 | + | let np; | |
| 1725 | + | if (np_item != -1) { | |
| 1726 | + | np = plman.GetPlayingItemLocation(); | |
| 1727 | + | if (np.IsValid) { | |
| 1728 | + | if (np.PlaylistIndex != pl_stnd_idx) pl_chk = false; | |
| 1729 | + | else pid = np.PlaylistItemIndex; | |
| 1730 | + | } | |
| 1731 | + | if (pl_chk && pid == -1 && items.Count < 5000) { | |
| 1732 | + | if (lib.ui.dui) plman.SetActivePlaylistContext(); | |
| 1733 | + | const start = Date.now(); | |
| 1734 | + | for (let i = 0; i < 20; i++) { | |
| 1735 | + | if (Date.now() - start > 300) break; | |
| 1736 | + | fb.RunMainMenuCommand('编辑/撤消'); | |
| 1737 | + | np = plman.GetPlayingItemLocation(); | |
| 1738 | + | if (np.IsValid) { | |
| 1739 | + | pid = np.PlaylistItemIndex; | |
| 1740 | + | if (pid != -1) break; | |
| 1741 | + | } | |
| 1742 | + | } | |
| 1743 | + | } | |
| 1744 | + | } | |
| 1745 | + | if (pid != -1) { | |
| 1746 | + | plman.ClearPlaylistSelection(pl_stnd_idx); | |
| 1747 | + | plman.SetPlaylistSelectionSingle(pl_stnd_idx, pid, true); | |
| 1748 | + | plman.RemovePlaylistSelection(pl_stnd_idx, true); | |
| 1749 | + | const it = items.Clone(); | |
| 1750 | + | items.RemoveRange(np_item, items.Count); | |
| 1751 | + | it.RemoveRange(0, np_item + 1); | |
| 1752 | + | if (plman.PlaylistItemCount(pl_stnd_idx) < 5000) plman.UndoBackup(pl_stnd_idx); | |
| 1753 | + | plman.InsertPlaylistItems(pl_stnd_idx, 0, items); | |
| 1754 | + | plman.InsertPlaylistItems(pl_stnd_idx, plman.PlaylistItemCount(pl_stnd_idx), it); | |
| 1755 | + | } else { | |
| 1756 | + | if (plman.PlaylistItemCount(pl_stnd_idx) < 5000) plman.UndoBackup(pl_stnd_idx); | |
| 1757 | + | plman.ClearPlaylist(pl_stnd_idx); | |
| 1758 | + | plman.InsertPlaylistItems(pl_stnd_idx, 0, items); | |
| 1759 | + | } | |
| 1760 | + | } | |
| 1761 | + | } else if (!add) { | |
| 1762 | + | if (plman.PlaylistItemCount(pl_stnd_idx) < 5000) plman.UndoBackup(pl_stnd_idx); | |
| 1763 | + | plman.ClearPlaylist(pl_stnd_idx); | |
| 1764 | + | plman.InsertPlaylistItems(pl_stnd_idx, 0, items); | |
| 1765 | + | plman.SetPlaylistFocusItem(pl_stnd_idx, 0); | |
| 1766 | + | } else { | |
| 1767 | + | if (plman.PlaylistItemCount(pl_stnd_idx) < 5000) plman.UndoBackup(pl_stnd_idx); | |
| 1768 | + | plman.InsertPlaylistItems(pl_stnd_idx, !insert ? plman.PlaylistItemCount(pl_stnd_idx) : plman.GetPlaylistFocusItemIndex(pl_stnd_idx), items, true); | |
| 1769 | + | const f_ix = !insert || plman.GetPlaylistFocusItemIndex(pl_stnd_idx) == -1 ? plman.PlaylistItemCount(pl_stnd_idx) - items.Count : plman.GetPlaylistFocusItemIndex(pl_stnd_idx) - items.Count; | |
| 1770 | + | plman.SetPlaylistFocusItem(pl_stnd_idx, f_ix); | |
| 1771 | + | plman.EnsurePlaylistItemVisible(pl_stnd_idx, f_ix); | |
| 1772 | + | } | |
| 1773 | + | if (autoPlay) { | |
| 1774 | + | const c = (plman.PlaybackOrder == 3 || plman.PlaybackOrder == 4) ? Math.ceil(plman.PlaylistItemCount(pl_stnd_idx) * Math.random() - 1) : 0; | |
| 1775 | + | plman.ExecutePlaylistDefaultAction(pl_stnd_idx, c); | |
| 1776 | + | } | |
| 1777 | + | } | |
| 1778 | + | ||
| 1779 | + | mbtn_dn() { | |
| 1780 | + | this.mbtn_dbl_clicked = false; | |
| 1781 | + | } | |
| 1782 | + | ||
| 1783 | + | mbtnDblClickOrAltDblClick(x, y, mask, type) { | |
| 1784 | + | this[`${type}_dbl_clicked`] = true; | |
| 1785 | + | if (type == 'mbtn' && (libSet.actionMode == 2 || libSet.mbtnClickAction == 2) || type == 'alt' && libSet.altClickAction == 2) { | |
| 1786 | + | const ix = this.get_ix(x, y, true, false); | |
| 1787 | + | if (ix < this.tree.length && ix >= 0) { | |
| 1788 | + | const handleList = new FbMetadbHandleList(); | |
| 1789 | + | this.range(this.tree[ix].item).forEach(v => { | |
| 1790 | + | if (v < lib.panel.list.Count) handleList.Add(lib.panel.list[v]); | |
| 1791 | + | }); | |
| 1792 | + | const queueHandles = plman.GetPlaybackQueueHandles(); | |
| 1793 | + | let remove = []; | |
| 1794 | + | for (let i = 0; i < handleList.Count; i++) { | |
| 1795 | + | for (let k = 0; k < queueHandles.Count; k++) { | |
| 1796 | + | if (handleList[i].Compare(queueHandles[k])) { | |
| 1797 | + | remove.push(k); | |
| 1798 | + | } | |
| 1799 | + | } | |
| 1800 | + | } | |
| 1801 | + | remove = [...new Set(remove)]; | |
| 1802 | + | plman.RemoveItemsFromPlaybackQueue(remove); | |
| 1803 | + | } | |
| 1804 | + | } | |
| 1805 | + | } | |
| 1806 | + | ||
| 1807 | + | mbtnUpOrAltClickUp(x, y, mask, type) { | |
| 1808 | + | if (this[`${type}_dbl_clicked`]) return; | |
| 1809 | + | if (type == 'mbtn' && (libSet.actionMode == 2 || libSet.mbtnClickAction == 2) || type == 'alt' && libSet.altClickAction == 2) { | |
| 1810 | + | setTimeout(() => { // timeout: wait & see if double click, but adds a little lag to single click: timeout can be commented out | |
| 1811 | + | if (this[`${type}_dbl_clicked`]) return; | |
| 1812 | + | const ix = this.get_ix(x, y, true, false); | |
| 1813 | + | if (ix < this.tree.length && ix >= 0) { | |
| 1814 | + | const handleList = new FbMetadbHandleList(); | |
| 1815 | + | this.range(this.tree[ix].item).forEach(v => { | |
| 1816 | + | if (v < lib.panel.list.Count) handleList.Add(lib.panel.list[v]); | |
| 1817 | + | }); | |
| 1818 | + | const add = new FbMetadbHandleList(); | |
| 1819 | + | handleList.Convert().forEach(h => { | |
| 1820 | + | let found = false; | |
| 1821 | + | plman.GetPlaybackQueueHandles().Convert().forEach(q => { | |
| 1822 | + | if (h.Compare(q)) found = true; | |
| 1823 | + | }); | |
| 1824 | + | if (!found) add.Add(h); | |
| 1825 | + | }); | |
| 1826 | + | const ap = plman.ActivePlaylist; | |
| 1827 | + | const plItems = plman.GetPlaylistItems(ap); | |
| 1828 | + | add.Convert().forEach(v => { | |
| 1829 | + | const ix = plItems.Find(v); | |
| 1830 | + | if (ix != -1) { | |
| 1831 | + | plman.AddPlaylistItemToPlaybackQueue(ap, ix); | |
| 1832 | + | } else plman.AddItemToPlaybackQueue(v); | |
| 1833 | + | }); | |
| 1834 | + | } | |
| 1835 | + | }, 180); | |
| 1836 | + | } else { | |
| 1837 | + | if (!libSet.libSource) return; | |
| 1838 | + | this.add(x, y, !libSet[`${type}ClickAction`]); | |
| 1839 | + | } | |
| 1840 | + | } | |
| 1841 | + | ||
| 1842 | + | merge(m, mergeBrCount) { | |
| 1843 | + | if (!libSet.libSource && !lib.panel.multiProcess) return; | |
| 1844 | + | const seen = {}; | |
| 1845 | + | for (let i = 0; i < m.length; i++) { | |
| 1846 | + | const v = m[i].srt[0].toUpperCase(); | |
| 1847 | + | if (seen[v] === undefined) seen[v] = i; | |
| 1848 | + | else { | |
| 1849 | + | if (!mergeBrCount) m[i].item.forEach(w => m[seen[v]].item.push(w)); | |
| 1850 | + | m.splice(i, 1); | |
| 1851 | + | i--; | |
| 1852 | + | } | |
| 1853 | + | } | |
| 1854 | + | } | |
| 1855 | + | ||
| 1856 | + | move(x, y) { | |
| 1857 | + | if (lib.but.Dn) return; | |
| 1858 | + | const ix = this.get_ix(x, y, false, false); | |
| 1859 | + | this.row.i = this.checkRow(x, y); | |
| 1860 | + | this.m.i = -1; | |
| 1861 | + | if (ix != -1) { | |
| 1862 | + | this.m.i = ix; | |
| 1863 | + | this.check_tooltip(ix, x, y); | |
| 1864 | + | } else if (this.countsRight || this.statisticsShow) { | |
| 1865 | + | this.check_tooltip(this.row.i, x, y); | |
| 1866 | + | } else this.deactivateTooltip(); | |
| 1867 | + | if (!libSet.mousePointerOnly) { | |
| 1868 | + | if (this.highlight.node || lib.panel.imgView) { | |
| 1869 | + | if (ix != -1 || this.inlineRoot && !this.m.br) this.hand = true; | |
| 1870 | + | } else if (this.m.br != -1 && !(this.inlineRoot && !this.m.br)) this.hand = true; | |
| 1871 | + | } | |
| 1872 | + | SetCursor(this.hand ? 'Hand' : !lib.but.Dn && y > lib.ui.y && y < lib.ui.y + lib.panel.search.h && libSet.searchShow && x > lib.ui.x + lib.but.q.h + lib.but.margin && x < lib.panel.search.x + lib.panel.search.w ? 'IBeam' : 'Arrow'); | |
| 1873 | + | const same = this.m.i == this.cur_ix && this.m.br == this.m.cur_br && this.row.i == this.row.cur; | |
| 1874 | + | if (same && !lib.sbar.touch.dn) return; | |
| 1875 | + | if (!lib.sbar.draw_timer && !same) lib.panel.treePaint(); | |
| 1876 | + | this.cur_ix = this.m.i; | |
| 1877 | + | this.m.cur_br = this.m.br; | |
| 1878 | + | this.row.cur = this.row.i; | |
| 1879 | + | } | |
| 1880 | + | ||
| 1881 | + | notifySelection(list) { | |
| 1882 | + | if (list === undefined) list = this.getHandleList('newItems'); | |
| 1883 | + | window.NotifyOthers(window.Name, list); | |
| 1884 | + | if (list.Count) return true; | |
| 1885 | + | } | |
| 1886 | + | ||
| 1887 | + | nowPlayingShow() { | |
| 1888 | + | if (this.nowp != -1) { | |
| 1889 | + | let np_i = -1; | |
| 1890 | + | for (let i = 0; i < this.tree.length; i++) { | |
| 1891 | + | const v = this.tree[i]; | |
| 1892 | + | if (this.inRange(this.nowp, v.item)) { | |
| 1893 | + | np_i = i; | |
| 1894 | + | if (!v.root) { | |
| 1895 | + | if (lib.panel.imgView) i = this.tree.length; | |
| 1896 | + | else if (!v.track) this.branch(this.tree[np_i]); | |
| 1897 | + | } | |
| 1898 | + | } | |
| 1899 | + | } | |
| 1900 | + | if (!lib.panel.imgView && !this.tree[np_i].root) { | |
| 1901 | + | this.clearSelected(); | |
| 1902 | + | if (!this.highlight.nowPlaying) this.tree[np_i].sel = true; | |
| 1903 | + | } | |
| 1904 | + | if (np_i != -1) this.showItem(np_i, 'np'); | |
| 1905 | + | } | |
| 1906 | + | } | |
| 1907 | + | ||
| 1908 | + | numSort(a, b) { | |
| 1909 | + | return a - b; | |
| 1910 | + | } | |
| 1911 | + | ||
| 1912 | + | on_char(code) { | |
| 1913 | + | if (lib.panel.search.active) return; | |
| 1914 | + | switch (code) { | |
| 1915 | + | case lib.vk.copy: { | |
| 1916 | + | const handleList = this.getHandleList('newItems'); | |
| 1917 | + | fb.CopyHandleListToClipboard(handleList); | |
| 1918 | + | break; | |
| 1919 | + | } | |
| 1920 | + | case lib.vk.selAll: | |
| 1921 | + | this.tree.forEach(v => { | |
| 1922 | + | if (!v.root) v.sel = true; | |
| 1923 | + | }); | |
| 1924 | + | this.getTreeSel(); | |
| 1925 | + | if (!this.sel_items.length) return; | |
| 1926 | + | this.setPlaylist(); | |
| 1927 | + | break; | |
| 1928 | + | case lib.vk.eFocusSearch: | |
| 1929 | + | case lib.vk.lFocusSearch: | |
| 1930 | + | if (libSet.searchShow) lib.search.focus(); | |
| 1931 | + | break; | |
| 1932 | + | case lib.vk.insert: | |
| 1933 | + | this.getTreeSel(); | |
| 1934 | + | if (!this.sel_items.length) return; | |
| 1935 | + | this.load(this.sel_items, true, true, false, false, true); | |
| 1936 | + | break; | |
| 1937 | + | } | |
| 1938 | + | } | |
| 1939 | + | ||
| 1940 | + | on_focus(p_is_focused) { | |
| 1941 | + | this.is_focused = p_is_focused; | |
| 1942 | + | if (p_is_focused && this.selList && this.selList.Count) this.selection_holder.SetSelection(this.selList); | |
| 1943 | + | } | |
| 1944 | + | ||
| 1945 | + | setPlaylist(ix, item) { | |
| 1946 | + | if (libSet.libSource) { | |
| 1947 | + | if (this.autoFill.key) this.load(this.sel_items, true, false, false, !libSet.sendToCur, false); | |
| 1948 | + | this.track(true); | |
| 1949 | + | } else if (this.autoFill.key) this.setPlaylistSelection(ix, item); | |
| 1950 | + | } | |
| 1951 | + | ||
| 1952 | + | on_key_down(vkey) { | |
| 1953 | + | if (vkey == lib.vk.collapseAll && !lib.panel.imgView) this.collapseAll(); | |
| 1954 | + | if (vkey == lib.vk.expand && !lib.panel.imgView) { | |
| 1955 | + | const isSel = this.tree.some(v => v.sel); | |
| 1956 | + | this.expand(); | |
| 1957 | + | if (isSel) { | |
| 1958 | + | lib.panel.setHeight(true); | |
| 1959 | + | this.checkAutoHeight(); | |
| 1960 | + | } | |
| 1961 | + | } | |
| 1962 | + | if (lib.panel.search.active) return; | |
| 1963 | + | if (lib.vk.k('enter')) { | |
| 1964 | + | if (!this.sel_items.length) return; | |
| 1965 | + | if (!libSet.libSource) { | |
| 1966 | + | if (this.autoPlay.send) plman.ExecutePlaylistDefaultAction($Lib.pl_active, this.sel_items[0]); | |
| 1967 | + | return; | |
| 1968 | + | } | |
| 1969 | + | switch (true) { | |
| 1970 | + | case lib.vk.k('shift'): | |
| 1971 | + | return this.load(this.sel_items, true, true, false, false, false); | |
| 1972 | + | case lib.vk.k('ctrl'): | |
| 1973 | + | return this.sendToNewPlaylist(); | |
| 1974 | + | default: | |
| 1975 | + | return this.load(this.sel_items, true, false, this.autoPlay.send, false, false); | |
| 1976 | + | } | |
| 1977 | + | } | |
| 1978 | + | let item = -1; | |
| 1979 | + | switch (vkey) { | |
| 1980 | + | case lib.vk.left: | |
| 1981 | + | if (lib.panel.imgView) lib.panel.pos -= 1; | |
| 1982 | + | lib.panel.pos = $Lib.clamp(lib.panel.pos, 0, this.tree.length - 1); | |
| 1983 | + | this.row.i = -1; | |
| 1984 | + | this.m.i = -1; | |
| 1985 | + | if (lib.panel.imgView) { | |
| 1986 | + | item = this.tree[lib.panel.pos]; | |
| 1987 | + | this.m.i = lib.panel.pos; | |
| 1988 | + | this.showItem(item.ix, 'left'); | |
| 1989 | + | this.setPlaylist(lib.panel.pos, item); | |
| 1990 | + | break; | |
| 1991 | + | } | |
| 1992 | + | // !imgView | |
| 1993 | + | if ((this.tree[lib.panel.pos].level == (this.rootNode ? 1 : 0)) && this.tree[lib.panel.pos].child.length < 1) { | |
| 1994 | + | item = this.tree[lib.panel.pos]; | |
| 1995 | + | this.m.i = lib.panel.pos = item.ix; | |
| 1996 | + | this.leftKeyCheckScroll(); | |
| 1997 | + | break; | |
| 1998 | + | } | |
| 1999 | + | if (this.tree[lib.panel.pos].child.length > 0) { | |
| 2000 | + | item = this.tree[lib.panel.pos]; | |
| 2001 | + | this.clearChild(item); | |
| 2002 | + | this.setTreeSel(item.ix); | |
| 2003 | + | this.m.i = lib.panel.pos = item.ix; | |
| 2004 | + | } else { | |
| 2005 | + | item = this.tree[this.tree[lib.panel.pos].par]; | |
| 2006 | + | if (item) this.clearChild(item); | |
| 2007 | + | this.setTreeSel(item.ix); | |
| 2008 | + | this.m.i = lib.panel.pos = item.ix; | |
| 2009 | + | } | |
| 2010 | + | lib.panel.treePaint(); | |
| 2011 | + | this.setPlaylist(lib.panel.pos, item); | |
| 2012 | + | lib.sbar.setRows(this.tree.length); | |
| 2013 | + | this.leftKeyCheckScroll(); | |
| 2014 | + | break; | |
| 2015 | + | case lib.vk.right: { | |
| 2016 | + | if (lib.panel.imgView) lib.panel.pos += 1; | |
| 2017 | + | lib.panel.pos = $Lib.clamp(lib.panel.pos, 0, this.tree.length - 1); | |
| 2018 | + | this.row.i = -1; | |
| 2019 | + | this.m.i = -1; | |
| 2020 | + | item = this.tree[lib.panel.pos]; | |
| 2021 | + | if (lib.panel.imgView) { | |
| 2022 | + | this.m.i = lib.panel.pos; | |
| 2023 | + | this.showItem(item.ix, 'right'); | |
| 2024 | + | this.setPlaylist(lib.panel.pos, item); | |
| 2025 | + | break; | |
| 2026 | + | } | |
| 2027 | + | // !imgView | |
| 2028 | + | if (item.child.length) { | |
| 2029 | + | lib.panel.pos++; | |
| 2030 | + | lib.panel.pos = $Lib.clamp(lib.panel.pos, !this.rootNode ? 0 : 1, this.tree.length - 1); | |
| 2031 | + | this.upDnKeyCheckScroll(vkey); | |
| 2032 | + | break; | |
| 2033 | + | } | |
| 2034 | + | if (libSet.autoCollapse) this.branchChange(item, false, true); | |
| 2035 | + | this.branch(item, !!item.root, true); | |
| 2036 | + | let n = 2; | |
| 2037 | + | if (item.child.length == 1 && libSet.treeAutoExpandSingle) { | |
| 2038 | + | this.branch(item.child[0], false, true); | |
| 2039 | + | n += item.child[0].child.length; | |
| 2040 | + | } | |
| 2041 | + | this.setTreeSel(item.ix); | |
| 2042 | + | lib.panel.treePaint(); | |
| 2043 | + | this.m.i = lib.panel.pos = item.ix; | |
| 2044 | + | this.setPlaylist(lib.panel.pos, item); | |
| 2045 | + | lib.sbar.setRows(this.tree.length); | |
| 2046 | + | const row = (lib.panel.pos * lib.ui.row.h - lib.sbar.scroll) / lib.ui.row.h; | |
| 2047 | + | if (row + n + item.child.length > lib.sbar.rows_drawn || row < 0) { | |
| 2048 | + | if (item.child.length > (lib.sbar.rows_drawn - n) || row < 0) lib.sbar.checkScroll(lib.panel.pos * lib.ui.row.h); | |
| 2049 | + | else lib.sbar.checkScroll(Math.min(lib.panel.pos * lib.ui.row.h, (lib.panel.pos + n - lib.sbar.rows_drawn + item.child.length) * lib.ui.row.h)); | |
| 2050 | + | } else lib.sbar.scrollRound(); | |
| 2051 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 2052 | + | break; | |
| 2053 | + | } | |
| 2054 | + | case lib.vk.pgUp: | |
| 2055 | + | if (this.tree.length == 0) break; | |
| 2056 | + | if (lib.panel.imgView) { | |
| 2057 | + | lib.panel.pos = lib.panel.pos - libImg.columns * (lib.panel.rows - 1); | |
| 2058 | + | lib.panel.pos = $Lib.clamp(lib.panel.pos, 0, this.tree.length - 1); | |
| 2059 | + | } else lib.panel.pos = Math.max(Math.round(lib.sbar.scroll / lib.ui.row.h + 0.4) - Math.floor(lib.panel.rows) + 1, !this.rootNode ? 0 : 1); | |
| 2060 | + | lib.sbar.pageThrottle(1); | |
| 2061 | + | this.setTreeSel(this.tree[lib.panel.pos].ix); | |
| 2062 | + | lib.panel.treePaint(); | |
| 2063 | + | this.setPlaylist(lib.panel.pos, this.tree[lib.panel.pos]); | |
| 2064 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 2065 | + | break; | |
| 2066 | + | ||
| 2067 | + | case lib.vk.pgDn: | |
| 2068 | + | if (this.tree.length == 0) break; | |
| 2069 | + | if (lib.panel.imgView) { | |
| 2070 | + | lib.panel.pos = lib.panel.pos + libImg.columns * (lib.panel.rows - 1); | |
| 2071 | + | lib.panel.pos = $Lib.clamp(lib.panel.pos, 0, this.tree.length - 1); | |
| 2072 | + | } else lib.panel.pos = Math.min(Math.round(lib.sbar.scroll / lib.ui.row.h + 0.4) + Math.floor(lib.panel.rows) * 2 - 2, this.tree.length - 1); | |
| 2073 | + | lib.sbar.pageThrottle(-1); | |
| 2074 | + | this.setTreeSel(this.tree[lib.panel.pos].ix); | |
| 2075 | + | lib.panel.treePaint(); | |
| 2076 | + | this.setPlaylist(lib.panel.pos, this.tree[lib.panel.pos]); | |
| 2077 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 2078 | + | break; | |
| 2079 | + | case lib.vk.home: | |
| 2080 | + | if (this.tree.length == 0) break; | |
| 2081 | + | lib.panel.pos = !this.rootNode ? 0 : 1; | |
| 2082 | + | lib.sbar.checkScroll(0, 'full'); | |
| 2083 | + | this.setTreeSel(this.tree[lib.panel.pos].ix); | |
| 2084 | + | lib.panel.treePaint(); | |
| 2085 | + | this.setPlaylist(lib.panel.pos, this.tree[lib.panel.pos]); | |
| 2086 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 2087 | + | break; | |
| 2088 | + | case lib.vk.end: | |
| 2089 | + | if (this.tree.length == 0) break; | |
| 2090 | + | lib.panel.pos = this.tree.length - 1; | |
| 2091 | + | lib.sbar.scrollToEnd(); | |
| 2092 | + | this.setTreeSel(this.tree[lib.panel.pos].ix); | |
| 2093 | + | lib.panel.treePaint(); | |
| 2094 | + | this.setPlaylist(lib.panel.pos, this.tree[lib.panel.pos]); | |
| 2095 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 2096 | + | break; | |
| 2097 | + | case lib.vk.dn: | |
| 2098 | + | case lib.vk.up: | |
| 2099 | + | if (this.tree.length == 0) break; | |
| 2100 | + | if ((lib.panel.pos == 0 && vkey == lib.vk.up) || (lib.panel.pos == this.tree.length - 1 && vkey == lib.vk.dn)) { | |
| 2101 | + | this.setTreeSel(-1); | |
| 2102 | + | break; | |
| 2103 | + | } | |
| 2104 | + | this.row.i = -1; | |
| 2105 | + | this.m.i = -1; | |
| 2106 | + | if (!lib.panel.imgView) { | |
| 2107 | + | if (vkey == lib.vk.dn) lib.panel.pos++; | |
| 2108 | + | if (vkey == lib.vk.up) lib.panel.pos--; | |
| 2109 | + | } else { | |
| 2110 | + | if (vkey == lib.vk.dn) lib.panel.pos += libImg.columns; | |
| 2111 | + | if (vkey == lib.vk.up) lib.panel.pos -= libImg.columns; | |
| 2112 | + | } | |
| 2113 | + | lib.panel.pos = $Lib.clamp(lib.panel.pos, !this.rootNode ? 0 : 1, this.tree.length - 1); | |
| 2114 | + | if (lib.panel.imgView) { | |
| 2115 | + | item = this.tree[lib.panel.pos]; | |
| 2116 | + | this.m.i = lib.panel.pos; | |
| 2117 | + | this.showItem(item.ix, vkey == lib.vk.up ? 'up' : 'down'); | |
| 2118 | + | this.setPlaylist(lib.panel.pos, item); | |
| 2119 | + | return; | |
| 2120 | + | } | |
| 2121 | + | this.upDnKeyCheckScroll(vkey); | |
| 2122 | + | break; | |
| 2123 | + | } | |
| 2124 | + | } | |
| 2125 | + | ||
| 2126 | + | on_main_menu(index) { | |
| 2127 | + | if (index == this.getMainMenuIndex.add) { | |
| 2128 | + | this.getTreeSel(); | |
| 2129 | + | if (!this.sel_items.length) return; | |
| 2130 | + | this.load(this.sel_items, true, true, false, false, false); | |
| 2131 | + | } | |
| 2132 | + | if (index == this.getMainMenuIndex.collapseAll) this.collapseAll(); | |
| 2133 | + | if (index == this.getMainMenuIndex.insert) { | |
| 2134 | + | this.getTreeSel(); | |
| 2135 | + | if (!this.sel_items.length) return; | |
| 2136 | + | this.load(this.sel_items, true, true, false, false, true); | |
| 2137 | + | } | |
| 2138 | + | if (index == this.getMainMenuIndex.new) { | |
| 2139 | + | this.getTreeSel(); | |
| 2140 | + | if (!this.sel_items.length) return; | |
| 2141 | + | this.sendToNewPlaylist(); | |
| 2142 | + | } | |
| 2143 | + | if (index == this.getMainMenuIndex.searchClear && libSet.searchShow) lib.search.clear(); | |
| 2144 | + | if (index == this.getMainMenuIndex.searchFocus && this.is_focused && libSet.searchShow) lib.search.focus(); | |
| 2145 | + | } | |
| 2146 | + | ||
| 2147 | + | range(item) { | |
| 2148 | + | const items = []; | |
| 2149 | + | item.forEach(v => { | |
| 2150 | + | for (let i = v.start; i <= v.end; i++) items.push(i); | |
| 2151 | + | }); | |
| 2152 | + | return items; | |
| 2153 | + | } | |
| 2154 | + | ||
| 2155 | + | removeDuplicateArr(arr) { | |
| 2156 | + | const t = {}; | |
| 2157 | + | return arr.filter(v => !(t[v] = v in t)); | |
| 2158 | + | } | |
| 2159 | + | ||
| 2160 | + | send(item, x, y) { | |
| 2161 | + | if (!this.check_ix(item, x, y, false)) return; | |
| 2162 | + | if (lib.vk.k('ctrl') || lib.vk.k('shift')) this.load(this.sel_items, true, false, false, !libSet.sendToCur, false); | |
| 2163 | + | else this.load(this.sel_items, true, false, this.autoPlay.click, !libSet.sendToCur, false); | |
| 2164 | + | } | |
| 2165 | + | ||
| 2166 | + | sendToNewPlaylist() { | |
| 2167 | + | const names = this.tree.filter(v => v.sel).map(v => v.name); | |
| 2168 | + | plman.ActivePlaylist = plman.CreatePlaylist(plman.PlaylistCount, [...new Set(names)].join('; ')); | |
| 2169 | + | this.load(this.sel_items, true, false, this.autoPlay.send, false, false); | |
| 2170 | + | } | |
| 2171 | + | ||
| 2172 | + | setActions() { | |
| 2173 | + | this.autoPlay = { | |
| 2174 | + | click: libSet.clickAction < 2 ? false : libSet.clickAction, | |
| 2175 | + | send: libSet.autoPlay | |
| 2176 | + | }; | |
| 2177 | + | this.autoFill = { | |
| 2178 | + | mouse: libSet.actionMode == 2 ? false : libSet.clickAction == 1 || libSet.actionMode == 1, | |
| 2179 | + | key: libSet.keyAction | |
| 2180 | + | }; | |
| 2181 | + | this.dblClickAction = libSet.actionMode == 1 ? 1 : libSet.actionMode == 2 ? 3 : libSet.dblClickAction; | |
| 2182 | + | } | |
| 2183 | + | ||
| 2184 | + | setPlaylistSelection(ix, item) { | |
| 2185 | + | this.clearSelected(); | |
| 2186 | + | if (!item.sel) this.setTreeSel(ix, item.sel); | |
| 2187 | + | lib.panel.treePaint(); | |
| 2188 | + | plman.ClearPlaylistSelection($Lib.pl_active); | |
| 2189 | + | let items = []; | |
| 2190 | + | if (lib.panel.search.txt || libSet.filterBy || lib.panel.multiProcess) { | |
| 2191 | + | const hl = this.getHandleList(); | |
| 2192 | + | hl.Convert().forEach(h => { | |
| 2193 | + | const i = lib.lib.full_list.Find(h); | |
| 2194 | + | if (i != -1) items.push(i); | |
| 2195 | + | }); | |
| 2196 | + | } else { | |
| 2197 | + | items = this.range(item.item); | |
| 2198 | + | } | |
| 2199 | + | plman.SetPlaylistSelection($Lib.pl_active, items, true); | |
| 2200 | + | this.setFocus = true; | |
| 2201 | + | plman.SetPlaylistFocusItem($Lib.pl_active, items[0]); | |
| 2202 | + | this.track(false); | |
| 2203 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 2204 | + | } | |
| 2205 | + | ||
| 2206 | + | setPos(pos) { | |
| 2207 | + | this.m.i = this.row.i = lib.panel.pos = pos; | |
| 2208 | + | } | |
| 2209 | + | ||
| 2210 | + | setTreeSel(idx, state) { | |
| 2211 | + | const sel_type = idx == -1 ? 0 : lib.vk.k('shift') && this.last_sel > -1 && libSet.libSource ? 1 : lib.vk.k('ctrl') ? 2 : !state ? 3 : 0; | |
| 2212 | + | switch (sel_type) { | |
| 2213 | + | case 0: | |
| 2214 | + | this.clearSelected(); | |
| 2215 | + | this.sel_items = []; | |
| 2216 | + | break; | |
| 2217 | + | case 1: { | |
| 2218 | + | const direction = (idx > this.last_sel) ? 1 : -1; | |
| 2219 | + | if (!lib.vk.k('ctrl')) this.clearSelected(); | |
| 2220 | + | for (let i = this.last_sel; ; i += direction) { | |
| 2221 | + | this.tree[i].sel = true; | |
| 2222 | + | if (i == idx) break; | |
| 2223 | + | } | |
| 2224 | + | this.getTreeSel(); | |
| 2225 | + | lib.panel.treePaint(); | |
| 2226 | + | break; | |
| 2227 | + | } | |
| 2228 | + | case 2: | |
| 2229 | + | this.tree[idx].sel = !this.tree[idx].sel; | |
| 2230 | + | this.getTreeSel(); | |
| 2231 | + | this.last_sel = idx; | |
| 2232 | + | break; | |
| 2233 | + | case 3: | |
| 2234 | + | this.sel_items = []; | |
| 2235 | + | this.clearSelected(); | |
| 2236 | + | this.tree[idx].sel = true; | |
| 2237 | + | this.addItems(this.sel_items, this.tree[idx].item); | |
| 2238 | + | this.uniq(this.sel_items); | |
| 2239 | + | this.last_sel = idx; | |
| 2240 | + | break; | |
| 2241 | + | } | |
| 2242 | + | } | |
| 2243 | + | ||
| 2244 | + | setValues() { | |
| 2245 | + | this.countsRight = libSet.countsRight; | |
| 2246 | + | this.fullLineSelection = libSet.fullLineSelection; | |
| 2247 | + | this.highlight = { | |
| 2248 | + | node: libSet.highLightNode, | |
| 2249 | + | nowPlaying: libSet.highLightNowplaying, | |
| 2250 | + | nowPlayingIndicator: libSet.nowPlayingIndicator, | |
| 2251 | + | nowPlayingSidemarker: libSet.nowPlayingSidemarker, | |
| 2252 | + | nowPlayingShow: libSet.highLightNowplaying || libSet.nowPlayingIndicator || libSet.nowPlayingSidemarker, | |
| 2253 | + | row: libSet.highLightRow, | |
| 2254 | + | text: libSet.highLightText | |
| 2255 | + | }; | |
| 2256 | + | this.iconVerticalPad = libSet.iconVerticalPad; | |
| 2257 | + | this.nodeCounts = libSet.nodeCounts; | |
| 2258 | + | this.nodeStyle = libSet.nodeStyle; | |
| 2259 | + | this.rootNode = libSet.rootNode; | |
| 2260 | + | this.rowStripes = libSet.rowStripes; | |
| 2261 | + | this.sbarShow = libSet.sbarShow; | |
| 2262 | + | this.showTracks = !libSet.facetView ? libSet.showTracks : false; | |
| 2263 | + | this.statistics = ['', '比特率', '持续时间', '合计大小', '等级', '热门', '日期', '播放队列', '播放次数', '首次播放', '最近播放', '添加时间']; | |
| 2264 | + | this.statisticsShow = libSet.itemShowStatistics; | |
| 2265 | + | this.label = !libSet.labelStatistics ? '' : this.statisticsShow ? this.statistics[this.statisticsShow] : ''; | |
| 2266 | + | this.tooltipStatistics = libSet.tooltipStatistics; | |
| 2267 | + | this.treeIndent = libSet.treeIndent; | |
| 2268 | + | this.imgGetItemCount = libSet.itemOverlayType != 1 && libSet.albumArtLabelType == 2 && !this.statisticsShow && (this.nodeCounts == 1 || this.nodeCounts == 2); | |
| 2269 | + | } | |
| 2270 | + | ||
| 2271 | + | showItem(i, type) { | |
| 2272 | + | if (!lib.panel.imgView) { | |
| 2273 | + | lib.sbar.checkScroll(i * lib.ui.row.h - Math.round(lib.sbar.rows_drawn / 2 - 1) * lib.ui.row.h, 'full'); | |
| 2274 | + | return; | |
| 2275 | + | } | |
| 2276 | + | this.m.i = -1; | |
| 2277 | + | const b = $Lib.clamp(Math.round(lib.sbar.delta / lib.sbar.row.h), 0, lib.panel.rows - 1); | |
| 2278 | + | const f = Math.min(b + Math.floor(libImg.panel.h / lib.sbar.row.h), lib.panel.rows); | |
| 2279 | + | this.setTreeSel(i); | |
| 2280 | + | lib.panel.treePaint(); | |
| 2281 | + | const row1 = Math.floor(i / (libImg.style.vertical ? libImg.columns : 1)); | |
| 2282 | + | if (row1 <= b || row1 >= f) { | |
| 2283 | + | switch (type) { | |
| 2284 | + | case 'np': | |
| 2285 | + | case 'focus': { | |
| 2286 | + | const delta = (libImg.style.vertical ? libImg.panel.h : libImg.panel.w) / 2 > lib.sbar.row.h ? Math.floor((libImg.style.vertical ? libImg.panel.h : libImg.panel.w) / 2) : 0; | |
| 2287 | + | const deltaRows = Math.floor(delta / lib.sbar.row.h) * lib.sbar.row.h; | |
| 2288 | + | lib.sbar.checkScroll((libImg.style.vertical ? row1 : i) * lib.sbar.row.h - deltaRows, 'full'); | |
| 2289 | + | break; | |
| 2290 | + | } | |
| 2291 | + | case 'up': | |
| 2292 | + | case 'down': | |
| 2293 | + | case 'left': | |
| 2294 | + | case 'right': { | |
| 2295 | + | const row2 = (row1 * lib.sbar.row.h - lib.sbar.scroll) / lib.sbar.row.h; | |
| 2296 | + | if (lib.sbar.rows_drawn - row2 < 1 || row2 < 0) lib.sbar.checkScroll((row1 + 1) * lib.sbar.row.h - lib.sbar.rows_drawn * lib.sbar.row.h); | |
| 2297 | + | else if (row2 < 1 & (type == 'up' || type == 'left')) lib.sbar.checkScroll(row1 * lib.sbar.row.h); | |
| 2298 | + | } | |
| 2299 | + | } | |
| 2300 | + | } | |
| 2301 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 2302 | + | } | |
| 2303 | + | ||
| 2304 | + | ||
| 2305 | + | sort(data) { | |
| 2306 | + | // 修复:ppt 和 panel 改为从 this 获取(作用域问题) | |
| 2307 | + | if (!this.ppt?.libSource && !this.panel?.multiProcess) return; | |
| 2308 | + | ||
| 2309 | + | this.specialCharSort(data); | |
| 2310 | + | ||
| 2311 | + | // 一次排序完成:类型排序 + 原有字段排序 + 字符串自然排序 | |
| 2312 | + | data.sort((a, b) => { | |
| 2313 | + | // ============= 1. 优先按【字符类型】排序(核心规则) ============= | |
| 2314 | + | const nameA = (a.srt[0] || '').trim(); | |
| 2315 | + | const nameB = (b.srt[0] || '').trim(); | |
| 2316 | + | ||
| 2317 | + | // 字符类型判断:符号(0) → 数字(1) → 英文(2) → 中文(3) → 其他(4) | |
| 2318 | + | const getCharType = (str) => { | |
| 2319 | + | if (!str) return 4; // 空字符串排最后 | |
| 2320 | + | const char = str.charAt(0); // 取第一个字符判断类型 | |
| 2321 | + | const code = char.charCodeAt(0); | |
| 2322 | + | ||
| 2323 | + | // 中文字符 | |
| 2324 | + | if (code >= 0x4e00 && code <= 0x9fff) return 3; | |
| 2325 | + | // 数字 0-9 | |
| 2326 | + | if (code >= 48 && code <= 57) return 1; | |
| 2327 | + | // 英文字母 A-Z a-z | |
| 2328 | + | if ((code >= 65 && code <= 90) || (code >= 97 && code <= 122)) return 2; | |
| 2329 | + | // 符号(非中文、非数字、非字母) | |
| 2330 | + | return 0; | |
| 2331 | + | }; | |
| 2332 | + | ||
| 2333 | + | const typeA = getCharType(nameA); | |
| 2334 | + | const typeB = getCharType(nameB); | |
| 2335 | + | ||
| 2336 | + | // 类型不同 → 按类型排序 | |
| 2337 | + | if (typeA !== typeB) return typeA - typeB; | |
| 2338 | + | ||
| 2339 | + | // ============= 2. 类型相同 → 按原有 collator 字段排序 ============= | |
| 2340 | + | const collatorCompare = this.collator.compare(a.srt[2], b.srt[2]); | |
| 2341 | + | if (collatorCompare !== 0) return collatorCompare; | |
| 2342 | + | ||
| 2343 | + | // ============= 3. 再按 srt[3] 布尔值排序 ============= | |
| 2344 | + | const flagCompare = (a.srt[3] && !b.srt[3]) ? 1 : (!a.srt[3] && b.srt[3]) ? -1 : 0; | |
| 2345 | + | if (flagCompare !== 0) return flagCompare; | |
| 2346 | + | ||
| 2347 | + | // ============= 4. 最后按字符串本身自然排序 ============= | |
| 2348 | + | return nameA.localeCompare(nameB, undefined, { numeric: true, sensitivity: 'base' }); | |
| 2349 | + | }); | |
| 2350 | + | } | |
| 2351 | + | ||
| 2352 | + | ||
| 2353 | + | sortIfNeeded(items) { | |
| 2354 | + | if (lib.panel.multiProcess && !libSet.customSort.length) items.OrderByFormat(lib.panel.playlistSort, 1); | |
| 2355 | + | else if (libSet.customSort.length) items.OrderByFormat(this.customSort, 1); | |
| 2356 | + | } | |
| 2357 | + | ||
| 2358 | + | sortViewByFolder(data) { | |
| 2359 | + | // Use Intl.Collator with numeric: true to sort file/folder names (srt[0]) naturally, | |
| 2360 | + | // matching Windows Explorer's alphanumeric order (e.g., "2.01" < "2.15", "1985a" < "1986 -"). | |
| 2361 | + | // This fixes incorrect track and folder sorting in View by Folder Structure. | |
| 2362 | + | const naturalCollator = new Intl.Collator(undefined, { numeric: true }); | |
| 2363 | + | ||
| 2364 | + | data.sort((a, b) => { | |
| 2365 | + | const nameCompare = naturalCollator.compare(a.srt[0], b.srt[0]); | |
| 2366 | + | if (nameCompare !== 0) return nameCompare; | |
| 2367 | + | // Fallback to metadata sorting (srt[2], srt[3]) for identical file/folder names. | |
| 2368 | + | return this.collator.compare(a.srt[2], b.srt[2]) || (a.srt[3] && !b.srt[3] ? 1 : 0); | |
| 2369 | + | }); | |
| 2370 | + | } | |
| 2371 | + | ||
| 2372 | + | specialCharHas(name) { | |
| 2373 | + | return RegExp(this.specialChar).test(name); | |
| 2374 | + | } | |
| 2375 | + | ||
| 2376 | + | specialCharIsLeading(name) { | |
| 2377 | + | return RegExp(`^${this.specialChar}`).test(name); | |
| 2378 | + | } | |
| 2379 | + | ||
| 2380 | + | specialCharPad(name) { | |
| 2381 | + | return name.replace(RegExp(`${this.specialChar}+`, 'g'), v => (`${v} `).slice(0, 5)); | |
| 2382 | + | } | |
| 2383 | + | ||
| 2384 | + | specialCharSort(data) { | |
| 2385 | + | const removed = []; | |
| 2386 | + | const filtered = data.filter((v, i) => { | |
| 2387 | + | const f = this.specialCharHas(v.srt[0]); | |
| 2388 | + | if (f) { | |
| 2389 | + | v.srt[1] = this.specialCharPad(v.srt[0]); | |
| 2390 | + | v.srt[2] = this.specialCharStrip(v.srt[0]); | |
| 2391 | + | v.srt[3] = this.specialCharIsLeading(v.srt[0]); | |
| 2392 | + | removed.push(i); | |
| 2393 | + | } | |
| 2394 | + | return f; | |
| 2395 | + | }); | |
| 2396 | + | removed.reverse(); | |
| 2397 | + | removed.forEach(v => data.splice(v, 1)); | |
| 2398 | + | const sorted = filtered.sort((a, b) => this.collator.compare(a.srt[1], b.srt[1])); | |
| 2399 | + | sorted.forEach(v => data.push(v)); | |
| 2400 | + | } | |
| 2401 | + | ||
| 2402 | + | specialCharStrip(name) { | |
| 2403 | + | let [str1, ...str2] = name.split(' '); | |
| 2404 | + | str2 = str2.join(' '); | |
| 2405 | + | if (this.isDate(str1)) return `${str1} ${str2.replace(RegExp(this.specialChar, 'g'), '')}`; | |
| 2406 | + | return name.replace(RegExp(this.specialChar, 'g'), ''); | |
| 2407 | + | } | |
| 2408 | + | ||
| 2409 | + | track(plLoaded) { | |
| 2410 | + | const list = this.getHandleList(); | |
| 2411 | + | this.notifySelection(list); | |
| 2412 | + | if (!plLoaded) this.selection_holder.SetSelection(list); | |
| 2413 | + | } | |
| 2414 | + | ||
| 2415 | + | trackCount(item) { | |
| 2416 | + | return item.reduce((a, b) => a + b.count, 0); | |
| 2417 | + | } | |
| 2418 | + | ||
| 2419 | + | treeTooltipFont() { | |
| 2420 | + | const libraryFontSize = SCALE((RES._4K ? grSet.libraryFontSize_layout - 0 : grSet.libraryFontSize_layout) || 14); | |
| 2421 | + | return !lib.panel.imgView ? [lib.ui.font.main.Name, /* ui.font.main.Size */ libraryFontSize + 3, lib.ui.font.main.Style] : [lib.ui.font.group.Name, /* ui.font.group.Size */ libraryFontSize + 3, lib.ui.font.group.Style]; | |
| 2422 | + | } | |
| 2423 | + | ||
| 2424 | + | uniq(arr) { | |
| 2425 | + | this.sel_items = [...new Set(arr)].sort(this.numSort); | |
| 2426 | + | } | |
| 2427 | + | ||
| 2428 | + | upDnKeyCheckScroll(vkey) { | |
| 2429 | + | const row = (lib.panel.pos * lib.ui.row.h - lib.sbar.scroll) / lib.ui.row.h; | |
| 2430 | + | if (lib.sbar.rows_drawn - row < 3 || row < 0) lib.sbar.checkScroll((lib.panel.pos + 3) * lib.ui.row.h - lib.sbar.rows_drawn * lib.ui.row.h); | |
| 2431 | + | else if (row < 2 && vkey == lib.vk.up) lib.sbar.checkScroll((lib.panel.pos - 1) * lib.ui.row.h); | |
| 2432 | + | this.m.i = lib.panel.pos; | |
| 2433 | + | this.setTreeSel(lib.panel.pos); | |
| 2434 | + | lib.panel.treePaint(); | |
| 2435 | + | this.setPlaylist(lib.panel.pos, this.tree[lib.panel.pos]); | |
| 2436 | + | lib.lib.treeState(false, libSet.rememberTree); | |
| 2437 | + | } | |
| 2438 | + | } | |