comparison foosdk/sdk/foobar2000/SDK/playlist.cpp @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
comparison
equal deleted inserted replaced
0:e9bb126753e7 1:20d02a178406
1 #include "foobar2000-sdk-pch.h"
2 #include "playlist.h"
3
4 namespace {
5 class enum_items_callback_func : public playlist_manager::enum_items_callback {
6 public:
7 bool on_item(t_size p_index, const metadb_handle_ptr& p_location, bool b_selected) override { return f(p_index, p_location, b_selected); }
8 playlist_manager::enum_items_func f;
9 };
10 class enum_items_callback_retrieve_item : public playlist_manager::enum_items_callback
11 {
12 metadb_handle_ptr m_item;
13 public:
14 enum_items_callback_retrieve_item() : m_item(0) {}
15 bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected)
16 {
17 (void)p_index; (void)b_selected;
18 PFC_ASSERT(m_item.is_empty());
19 m_item = p_location;
20 return false;
21 }
22 inline const metadb_handle_ptr & get_item() {return m_item;}
23 };
24
25 class enum_items_callback_retrieve_selection : public playlist_manager::enum_items_callback
26 {
27 bool m_state;
28 public:
29 enum_items_callback_retrieve_selection() : m_state(false) {}
30 bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected)
31 {
32 (void)p_index; (void)p_location;
33 m_state = b_selected;
34 return false;
35 }
36 inline bool get_state() {return m_state;}
37 };
38
39 class enum_items_callback_retrieve_selection_mask : public playlist_manager::enum_items_callback
40 {
41 bit_array_var & m_out;
42 public:
43 enum_items_callback_retrieve_selection_mask(bit_array_var & p_out) : m_out(p_out) {}
44 bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected)
45 {
46 (void)p_location;
47 m_out.set(p_index,b_selected);
48 return true;
49 }
50 };
51
52 class enum_items_callback_retrieve_all_items : public playlist_manager::enum_items_callback
53 {
54 pfc::list_base_t<metadb_handle_ptr> & m_out;
55 public:
56 enum_items_callback_retrieve_all_items(pfc::list_base_t<metadb_handle_ptr> & p_out) : m_out(p_out) {m_out.remove_all();}
57 bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected)
58 {
59 (void)p_index; (void)b_selected;
60 m_out.add_item(p_location);
61 return true;
62 }
63 };
64
65 class enum_items_callback_retrieve_selected_items : public playlist_manager::enum_items_callback
66 {
67 pfc::list_base_t<metadb_handle_ptr> & m_out;
68 public:
69 enum_items_callback_retrieve_selected_items(pfc::list_base_t<metadb_handle_ptr> & p_out) : m_out(p_out) {m_out.remove_all();}
70 bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected)
71 {
72 (void)p_index;
73 if (b_selected) m_out.add_item(p_location);
74 return true;
75 }
76 };
77
78 class enum_items_callback_count_selection : public playlist_manager::enum_items_callback
79 {
80 t_size m_counter,m_max;
81 public:
82 enum_items_callback_count_selection(t_size p_max) : m_max(p_max), m_counter(0) {}
83 bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected)
84 {
85 (void)p_index; (void)p_location;
86 if (b_selected)
87 {
88 if (++m_counter >= m_max) return false;
89 }
90 return true;
91 }
92
93 inline t_size get_count() {return m_counter;}
94 };
95
96 }
97
98 void playlist_manager::playlist_get_all_items(t_size p_playlist,pfc::list_base_t<metadb_handle_ptr> & out)
99 {
100 playlist_get_items(p_playlist,out, pfc::bit_array_true());
101 }
102
103 void playlist_manager::playlist_get_selected_items(t_size p_playlist,pfc::list_base_t<metadb_handle_ptr> & out)
104 {
105 enum_items_callback_retrieve_selected_items cb(out);
106 playlist_enum_items(p_playlist,cb,pfc::bit_array_true());
107 }
108
109 void playlist_manager::playlist_get_selection_mask(t_size p_playlist,bit_array_var & out)
110 {
111 enum_items_callback_retrieve_selection_mask cb(out);
112 playlist_enum_items(p_playlist,cb,pfc::bit_array_true());
113 }
114
115 bool playlist_manager::playlist_is_item_selected(t_size p_playlist,t_size p_item)
116 {
117 enum_items_callback_retrieve_selection callback;
118 playlist_enum_items(p_playlist,callback,pfc::bit_array_one(p_item));
119 return callback.get_state();
120 }
121
122 metadb_handle_ptr playlist_manager::playlist_get_item_handle(t_size playlist, t_size item) {
123 metadb_handle_ptr temp;
124 if (!playlist_get_item_handle(temp, playlist, item)) throw pfc::exception_invalid_params();
125 PFC_ASSERT( temp.is_valid() );
126 return temp;
127
128 }
129 bool playlist_manager::playlist_get_item_handle(metadb_handle_ptr & p_out,t_size p_playlist,t_size p_item)
130 {
131 enum_items_callback_retrieve_item callback;
132 playlist_enum_items(p_playlist,callback,pfc::bit_array_one(p_item));
133 p_out = callback.get_item();
134 return p_out.is_valid();
135 }
136
137 void playlist_manager::g_make_selection_move_permutation(t_size * p_output,t_size p_count,const bit_array & p_selection,int p_delta) {
138 pfc::create_move_items_permutation(p_output,p_count,p_selection,p_delta);
139 }
140
141 bool playlist_manager::playlist_move_selection(t_size p_playlist,int p_delta) {
142 if (p_delta==0) return true;
143
144 t_size count = playlist_get_item_count(p_playlist);
145
146 pfc::array_t<t_size> order; order.set_size(count);
147 pfc::array_t<bool> selection; selection.set_size(count);
148
149 pfc::bit_array_var_table mask(selection.get_ptr(),selection.get_size());
150 playlist_get_selection_mask(p_playlist, mask);
151 g_make_selection_move_permutation(order.get_ptr(),count,mask,p_delta);
152 return playlist_reorder_items(p_playlist,order.get_ptr(),count);
153 }
154
155 //retrieving status
156 t_size playlist_manager::activeplaylist_get_item_count()
157 {
158 t_size playlist = get_active_playlist();
159 if (playlist == SIZE_MAX) return 0;
160 else return playlist_get_item_count(playlist);
161 }
162
163 void playlist_manager::playlist_enum_items(size_t which, enum_items_func f, const bit_array& mask) {
164 enum_items_callback_func cb; cb.f = f;
165 this->playlist_enum_items(which, cb, mask);
166 }
167
168 void playlist_manager::activeplaylist_enum_items(enum_items_callback & p_callback,const bit_array & p_mask)
169 {
170 t_size playlist = get_active_playlist();
171 if (playlist != SIZE_MAX) playlist_enum_items(playlist,p_callback,p_mask);
172 }
173 void playlist_manager::activeplaylist_enum_items(enum_items_func f, const bit_array& mask) {
174 size_t playlist = get_active_playlist();
175 if (playlist != SIZE_MAX) playlist_enum_items(playlist, f, mask);
176 }
177 t_size playlist_manager::activeplaylist_get_focus_item()
178 {
179 t_size playlist = get_active_playlist();
180 if (playlist == SIZE_MAX) return SIZE_MAX;
181 else return playlist_get_focus_item(playlist);
182 }
183
184 bool playlist_manager::activeplaylist_get_name(pfc::string_base & p_out)
185 {
186 t_size playlist = get_active_playlist();
187 if (playlist == SIZE_MAX) return false;
188 else return playlist_get_name(playlist,p_out);
189 }
190
191 //modifying playlist
192 bool playlist_manager::activeplaylist_reorder_items(const t_size * order,t_size count)
193 {
194 t_size playlist = get_active_playlist();
195 if (playlist != SIZE_MAX) return playlist_reorder_items(playlist,order,count);
196 else return false;
197 }
198
199 void playlist_manager::activeplaylist_set_selection(const bit_array & affected,const bit_array & status)
200 {
201 t_size playlist = get_active_playlist();
202 if (playlist != SIZE_MAX) playlist_set_selection(playlist,affected,status);
203 }
204
205 bool playlist_manager::activeplaylist_remove_items(const bit_array & mask)
206 {
207 t_size playlist = get_active_playlist();
208 if (playlist != SIZE_MAX) return playlist_remove_items(playlist,mask);
209 else return false;
210 }
211
212 bool playlist_manager::activeplaylist_replace_item(t_size p_item,const metadb_handle_ptr & p_new_item)
213 {
214 t_size playlist = get_active_playlist();
215 if (playlist != SIZE_MAX) return playlist_replace_item(playlist,p_item,p_new_item);
216 else return false;
217 }
218
219 void playlist_manager::activeplaylist_set_focus_item(t_size p_item)
220 {
221 t_size playlist = get_active_playlist();
222 if (playlist != SIZE_MAX) playlist_set_focus_item(playlist,p_item);
223 }
224
225 t_size playlist_manager::activeplaylist_insert_items(t_size p_base,const pfc::list_base_const_t<metadb_handle_ptr> & data,const bit_array & p_selection)
226 {
227 t_size playlist = get_active_playlist();
228 if (playlist != SIZE_MAX) return playlist_insert_items(playlist,p_base,data,p_selection);
229 else return SIZE_MAX;
230 }
231
232 void playlist_manager::activeplaylist_ensure_visible(t_size p_item)
233 {
234 t_size playlist = get_active_playlist();
235 if (playlist != SIZE_MAX) playlist_ensure_visible(playlist,p_item);
236 }
237
238 bool playlist_manager::activeplaylist_rename(const char * p_name,t_size p_name_len)
239 {
240 t_size playlist = get_active_playlist();
241 if (playlist != SIZE_MAX) return playlist_rename(playlist,p_name,p_name_len);
242 else return false;
243 }
244
245 bool playlist_manager::activeplaylist_is_item_selected(t_size p_item)
246 {
247 t_size playlist = get_active_playlist();
248 if (playlist != pfc_infinite) return playlist_is_item_selected(playlist,p_item);
249 else return false;
250 }
251
252 metadb_handle_ptr playlist_manager::activeplaylist_get_item_handle(t_size p_item) {
253 metadb_handle_ptr temp;
254 if (!activeplaylist_get_item_handle(temp, p_item)) throw pfc::exception_invalid_params();
255 PFC_ASSERT( temp.is_valid() );
256 return temp;
257 }
258 bool playlist_manager::activeplaylist_get_item_handle(metadb_handle_ptr & p_out,t_size p_item)
259 {
260 t_size playlist = get_active_playlist();
261 if (playlist != SIZE_MAX) return playlist_get_item_handle(p_out,playlist,p_item);
262 else return false;
263 }
264
265 void playlist_manager::activeplaylist_move_selection(int p_delta)
266 {
267 t_size playlist = get_active_playlist();
268 if (playlist != SIZE_MAX) playlist_move_selection(playlist,p_delta);
269 }
270
271 void playlist_manager::activeplaylist_get_selection_mask(bit_array_var & out)
272 {
273 t_size playlist = get_active_playlist();
274 if (playlist != SIZE_MAX) playlist_get_selection_mask(playlist,out);
275 }
276
277 void playlist_manager::activeplaylist_get_all_items(pfc::list_base_t<metadb_handle_ptr> & out)
278 {
279 t_size playlist = get_active_playlist();
280 if (playlist != SIZE_MAX) playlist_get_all_items(playlist,out);
281 }
282
283 void playlist_manager::activeplaylist_get_selected_items(pfc::list_base_t<metadb_handle_ptr> & out)
284 {
285 t_size playlist = get_active_playlist();
286 if (playlist != SIZE_MAX) playlist_get_selected_items(playlist,out);
287 }
288
289 bool playlist_manager::remove_playlist(t_size idx)
290 {
291 return remove_playlists(pfc::bit_array_one(idx));
292 }
293
294 bool playlist_incoming_item_filter::process_location(const char * url,pfc::list_base_t<metadb_handle_ptr> & out,bool filter,const char * p_mask,const char * p_exclude,fb2k::hwnd_t p_parentwnd)
295 {
296 return process_locations(pfc::list_single_ref_t<const char*>(url),out,filter,p_mask,p_exclude,p_parentwnd);
297 }
298
299 void playlist_manager::playlist_clear(t_size p_playlist)
300 {
301 playlist_remove_items(p_playlist, pfc::bit_array_true());
302 }
303
304 void playlist_manager::activeplaylist_clear()
305 {
306 t_size playlist = get_active_playlist();
307 if (playlist != SIZE_MAX) playlist_clear(playlist);
308 }
309
310 bool playlist_manager::playlist_update_content(t_size playlist, metadb_handle_list_cref content, bool bUndoBackup) {
311 metadb_handle_list old;
312 playlist_get_all_items(playlist, old);
313 if (old.get_size() == 0) {
314 if (content.get_size() == 0) return false;
315 if (bUndoBackup) playlist_undo_backup(playlist);
316 playlist_add_items(playlist, content, pfc::bit_array_false());
317 return true;
318 }
319 pfc::avltree_t<metadb_handle::nnptr> itemsOld, itemsNew;
320
321 for(t_size walk = 0; walk < old.get_size(); ++walk) itemsOld += old[walk];
322 for(t_size walk = 0; walk < content.get_size(); ++walk) itemsNew += content[walk];
323 pfc::bit_array_bittable removeMask(old.get_size());
324 pfc::bit_array_bittable filterMask(content.get_size());
325 bool gotNew = false, filterNew = false, gotRemove = false;
326 for(t_size walk = 0; walk < content.get_size(); ++walk) {
327 const bool state = !itemsOld.have_item(content[walk]);
328 if (state) gotNew = true;
329 else filterNew = true;
330 filterMask.set(walk, state);
331 }
332 for(t_size walk = 0; walk < old.get_size(); ++walk) {
333 const bool state = !itemsNew.have_item(old[walk]);
334 if (state) gotRemove = true;
335 removeMask.set(walk, state);
336 }
337 if (!gotNew && !gotRemove) return false;
338 if (bUndoBackup) playlist_undo_backup(playlist);
339 if (gotRemove) {
340 playlist_remove_items(playlist, removeMask);
341 }
342 if (gotNew) {
343 if (filterNew) {
344 metadb_handle_list temp(content);
345 temp.filter_mask(filterMask);
346 playlist_add_items(playlist, temp, pfc::bit_array_false());
347 } else {
348 playlist_add_items(playlist, content, pfc::bit_array_false());
349 }
350 }
351
352 {
353 playlist_get_all_items(playlist, old);
354 pfc::array_t<t_size> order;
355 if (pfc::guess_reorder_pattern<pfc::list_base_const_t<metadb_handle_ptr> >(order, old, content)) {
356 playlist_reorder_items(playlist, order.get_ptr(), order.get_size());
357 }
358 }
359 return true;
360 }
361 bool playlist_manager::playlist_add_items(t_size playlist,const pfc::list_base_const_t<metadb_handle_ptr> & data,const bit_array & p_selection)
362 {
363 return playlist_insert_items(playlist, SIZE_MAX, data, p_selection) != SIZE_MAX;
364 }
365
366 bool playlist_manager::activeplaylist_add_items(const pfc::list_base_const_t<metadb_handle_ptr> & data,const bit_array & p_selection)
367 {
368 t_size playlist = get_active_playlist();
369 if (playlist != SIZE_MAX) return playlist_add_items(playlist,data,p_selection);
370 else return false;
371 }
372
373 bool playlist_manager::playlist_insert_items_filter(t_size p_playlist,t_size p_base,const pfc::list_base_const_t<metadb_handle_ptr> & p_data,bool p_select)
374 {
375 metadb_handle_list temp;
376 if (!playlist_incoming_item_filter::get()->filter_items(p_data,temp))
377 return false;
378 return playlist_insert_items(p_playlist,p_base,temp, pfc::bit_array_val(p_select)) != SIZE_MAX;
379 }
380
381 bool playlist_manager::activeplaylist_insert_items_filter(t_size p_base,const pfc::list_base_const_t<metadb_handle_ptr> & p_data,bool p_select)
382 {
383 t_size playlist = get_active_playlist();
384 if (playlist != SIZE_MAX) return playlist_insert_items_filter(playlist,p_base,p_data,p_select);
385 else return false;
386 }
387
388 bool playlist_manager::playlist_insert_locations(t_size p_playlist,t_size p_base,const pfc::list_base_const_t<const char*> & p_urls,bool p_select,fb2k::hwnd_t p_parentwnd)
389 {
390 metadb_handle_list temp;
391 if (!playlist_incoming_item_filter::get()->process_locations(p_urls,temp,true,0,0,p_parentwnd)) return false;
392 return playlist_insert_items(p_playlist,p_base,temp, pfc::bit_array_val(p_select)) != SIZE_MAX;
393 }
394
395 bool playlist_manager::activeplaylist_insert_locations(t_size p_base,const pfc::list_base_const_t<const char*> & p_urls,bool p_select,fb2k::hwnd_t p_parentwnd)
396 {
397 t_size playlist = get_active_playlist();
398 if (playlist != SIZE_MAX) return playlist_insert_locations(playlist,p_base,p_urls,p_select,p_parentwnd);
399 else return false;
400 }
401
402 bool playlist_manager::playlist_add_items_filter(t_size p_playlist,const pfc::list_base_const_t<metadb_handle_ptr> & p_data,bool p_select)
403 {
404 return playlist_insert_items_filter(p_playlist,SIZE_MAX,p_data,p_select);
405 }
406
407 bool playlist_manager::activeplaylist_add_items_filter(const pfc::list_base_const_t<metadb_handle_ptr> & p_data,bool p_select)
408 {
409 return activeplaylist_insert_items_filter(SIZE_MAX,p_data,p_select);
410 }
411
412 bool playlist_manager::playlist_add_locations(t_size p_playlist,const pfc::list_base_const_t<const char*> & p_urls,bool p_select,fb2k::hwnd_t p_parentwnd)
413 {
414 return playlist_insert_locations(p_playlist,SIZE_MAX,p_urls,p_select,p_parentwnd);
415 }
416 bool playlist_manager::activeplaylist_add_locations(const pfc::list_base_const_t<const char*> & p_urls,bool p_select,fb2k::hwnd_t p_parentwnd)
417 {
418 return activeplaylist_insert_locations(SIZE_MAX,p_urls,p_select,p_parentwnd);
419 }
420
421 void playlist_manager::reset_playing_playlist()
422 {
423 set_playing_playlist(get_active_playlist());
424 }
425
426 void playlist_manager::playlist_clear_selection(t_size p_playlist)
427 {
428 playlist_set_selection(p_playlist, pfc::bit_array_true(), pfc::bit_array_false());
429 }
430
431 void playlist_manager::activeplaylist_clear_selection()
432 {
433 t_size playlist = get_active_playlist();
434 if (playlist != SIZE_MAX) playlist_clear_selection(playlist);
435 }
436
437 void playlist_manager::activeplaylist_undo_backup()
438 {
439 t_size playlist = get_active_playlist();
440 if (playlist != SIZE_MAX) playlist_undo_backup(playlist);
441 }
442
443 bool playlist_manager::activeplaylist_undo_restore()
444 {
445 t_size playlist = get_active_playlist();
446 if (playlist != SIZE_MAX) return playlist_undo_restore(playlist);
447 else return false;
448 }
449
450 bool playlist_manager::activeplaylist_redo_restore()
451 {
452 t_size playlist = get_active_playlist();
453 if (playlist != SIZE_MAX) return playlist_redo_restore(playlist);
454 else return false;
455 }
456
457 void playlist_manager::playlist_remove_selection(t_size p_playlist,bool p_crop)
458 {
459 pfc::bit_array_bittable table(playlist_get_item_count(p_playlist));
460 playlist_get_selection_mask(p_playlist,table);
461 if (p_crop) playlist_remove_items(p_playlist, pfc::bit_array_not(table));
462 else playlist_remove_items(p_playlist,table);
463 }
464
465 void playlist_manager::activeplaylist_remove_selection(bool p_crop)
466 {
467 t_size playlist = get_active_playlist();
468 if (playlist != SIZE_MAX) playlist_remove_selection(playlist,p_crop);
469 }
470
471 void playlist_manager::activeplaylist_item_format_title(t_size p_item,titleformat_hook * p_hook,pfc::string_base & out,const service_ptr_t<titleformat_object> & p_script,titleformat_text_filter * p_filter,play_control::t_display_level p_playback_info_level)
472 {
473 t_size playlist = get_active_playlist();
474 if (playlist == SIZE_MAX) out = "NJET";
475 else playlist_item_format_title(playlist,p_item,p_hook,out,p_script,p_filter,p_playback_info_level);
476 }
477
478 void playlist_manager::playlist_set_selection_single(t_size p_playlist,t_size p_item,bool p_state)
479 {
480 playlist_set_selection(p_playlist, pfc::bit_array_one(p_item), pfc::bit_array_val(p_state));
481 }
482
483 void playlist_manager::activeplaylist_set_selection_single(t_size p_item,bool p_state)
484 {
485 t_size playlist = get_active_playlist();
486 if (playlist != SIZE_MAX) playlist_set_selection_single(playlist,p_item,p_state);
487 }
488
489 t_size playlist_manager::playlist_get_selection_count(t_size p_playlist,t_size p_max)
490 {
491 enum_items_callback_count_selection callback(p_max);
492 playlist_enum_items(p_playlist,callback, pfc::bit_array_true());
493 return callback.get_count();
494 }
495
496 t_size playlist_manager::activeplaylist_get_selection_count(t_size p_max)
497 {
498 t_size playlist = get_active_playlist();
499 if (playlist != SIZE_MAX) return playlist_get_selection_count(playlist,p_max);
500 else return 0;
501 }
502
503 bool playlist_manager::playlist_get_focus_item_handle(metadb_handle_ptr & p_out,t_size p_playlist)
504 {
505 t_size index = playlist_get_focus_item(p_playlist);
506 if (index == SIZE_MAX) return false;
507 return playlist_get_item_handle(p_out,p_playlist,index);
508 }
509
510 bool playlist_manager::activeplaylist_get_focus_item_handle(metadb_handle_ptr & p_out)
511 {
512 t_size playlist = get_active_playlist();
513 if (playlist != SIZE_MAX) return playlist_get_focus_item_handle(p_out,playlist);
514 else return false;
515 }
516
517 t_size playlist_manager::find_playlist(const char * p_name,t_size p_name_length)
518 {
519 t_size n, m = get_playlist_count();
520 pfc::string_formatter temp;
521 for(n=0;n<m;n++) {
522 if (!playlist_get_name(n,temp)) break;
523 if (stricmp_utf8_ex(temp,temp.length(),p_name,p_name_length) == 0) return n;
524 }
525 return SIZE_MAX;
526 }
527
528 t_size playlist_manager::find_or_create_playlist_unlocked(const char * p_name, t_size p_name_length) {
529 t_size n, m = get_playlist_count();
530 pfc::string_formatter temp;
531 for(n=0;n<m;n++) {
532 if (!playlist_lock_is_present(n) && playlist_get_name(n,temp)) {
533 if (stricmp_utf8_ex(temp,SIZE_MAX,p_name,p_name_length) == 0) return n;
534 }
535 }
536 return create_playlist(p_name,p_name_length, SIZE_MAX);
537 }
538 t_size playlist_manager::find_or_create_playlist(const char * p_name,t_size p_name_length)
539 {
540 t_size index = find_playlist(p_name,p_name_length);
541 if (index != SIZE_MAX) return index;
542 return create_playlist(p_name,p_name_length, SIZE_MAX);
543 }
544
545 t_size playlist_manager::create_playlist_autoname(t_size p_index) {
546 static const char new_playlist_text[] = "New Playlist";
547 if (find_playlist(new_playlist_text, SIZE_MAX) == SIZE_MAX) return create_playlist(new_playlist_text,SIZE_MAX,p_index);
548 for(t_size walk = 2; ; walk++) {
549 pfc::string_fixed_t<64> namebuffer;
550 namebuffer << new_playlist_text << " (" << walk << ")";
551 if (find_playlist(namebuffer, SIZE_MAX) == SIZE_MAX) return create_playlist(namebuffer,SIZE_MAX,p_index);
552 }
553 }
554
555 bool playlist_manager::activeplaylist_sort_by_format(const char * spec,bool p_sel_only)
556 {
557 t_size playlist = get_active_playlist();
558 if (playlist != pfc_infinite) return playlist_sort_by_format(playlist,spec,p_sel_only);
559 else return false;
560 }
561
562 bool playlist_manager::highlight_playing_item()
563 {
564 t_size playlist,item;
565 if (!get_playing_item_location(&playlist,&item)) return false;
566 set_active_playlist(playlist);
567 playlist_set_focus_item(playlist,item);
568 playlist_set_selection(playlist, pfc::bit_array_true(), pfc::bit_array_one(item));
569 playlist_ensure_visible(playlist,item);
570 return true;
571 }
572
573 void playlist_manager::playlist_get_items(t_size p_playlist,pfc::list_base_t<metadb_handle_ptr> & out,const bit_array & p_mask)
574 {
575 enum_items_callback_retrieve_all_items cb(out);
576 playlist_enum_items(p_playlist,cb,p_mask);
577 }
578
579 void playlist_manager::activeplaylist_get_items(pfc::list_base_t<metadb_handle_ptr> & out,const bit_array & p_mask)
580 {
581 t_size playlist = get_active_playlist();
582 if (playlist != pfc_infinite) playlist_get_items(playlist,out,p_mask);
583 else out.remove_all();
584 }
585
586 void playlist_manager::active_playlist_fix()
587 {
588 t_size playlist = get_active_playlist();
589 if (playlist == pfc_infinite)
590 {
591 t_size max = get_playlist_count();
592 if (max == 0)
593 {
594 create_playlist_autoname();
595 }
596 set_active_playlist(0);
597 }
598 }
599
600 namespace {
601 class enum_items_callback_remove_list : public playlist_manager::enum_items_callback
602 {
603 const metadb_handle_list & m_data;
604 bit_array_var & m_table;
605 t_size m_found;
606 public:
607 enum_items_callback_remove_list(const metadb_handle_list & p_data,bit_array_var & p_table) : m_data(p_data), m_table(p_table), m_found(0) {}
608 bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) override
609 {
610 (void)b_selected;
611 bool found = m_data.bsearch_by_pointer(p_location) != pfc_infinite;
612 m_table.set(p_index,found);
613 if (found) m_found++;
614 return true;
615 }
616
617 inline t_size get_found() const {return m_found;}
618 };
619 }
620
621 void playlist_manager::remove_items_from_all_playlists(const pfc::list_base_const_t<metadb_handle_ptr> & p_data)
622 {
623 t_size playlist_num, playlist_max = get_playlist_count();
624 if (playlist_max != pfc_infinite)
625 {
626 metadb_handle_list temp;
627 temp.add_items(p_data);
628 temp.sort_by_pointer();
629 for(playlist_num = 0; playlist_num < playlist_max; playlist_num++ )
630 {
631 t_size playlist_item_count = playlist_get_item_count(playlist_num);
632 if (playlist_item_count == pfc_infinite) break;
633 pfc::bit_array_bittable table(playlist_item_count);
634 enum_items_callback_remove_list callback(temp,table);
635 playlist_enum_items(playlist_num,callback, pfc::bit_array_true());
636 if (callback.get_found()>0)
637 playlist_remove_items(playlist_num,table);
638 }
639 }
640 }
641
642 bool playlist_manager::get_all_items(pfc::list_base_t<metadb_handle_ptr> & out)
643 {
644 t_size n, m = get_playlist_count();
645 if (m == pfc_infinite) return false;
646 enum_items_callback_retrieve_all_items callback(out);
647 for(n=0;n<m;n++)
648 {
649 playlist_enum_items(n,callback,pfc::bit_array_true());
650 }
651 return true;
652 }
653
654 t_uint32 playlist_manager::activeplaylist_lock_get_filter_mask()
655 {
656 t_size playlist = get_active_playlist();
657 if (playlist == SIZE_MAX) return UINT32_MAX;
658 else return playlist_lock_get_filter_mask(playlist);
659 }
660
661 bool playlist_manager::activeplaylist_is_undo_available()
662 {
663 t_size playlist = get_active_playlist();
664 if (playlist == pfc_infinite) return false;
665 else return playlist_is_undo_available(playlist);
666 }
667
668 bool playlist_manager::activeplaylist_is_redo_available()
669 {
670 t_size playlist = get_active_playlist();
671 if (playlist == pfc_infinite) return false;
672 else return playlist_is_redo_available(playlist);
673 }
674
675 bool playlist_manager::remove_playlist_user() {
676 size_t a = this->get_active_playlist();
677 if (a == SIZE_MAX) {
678 // FIX ME implement toast
679 #ifdef _WIN32
680 MessageBeep(0);
681 #endif
682 return false;
683 }
684 return this->remove_playlist_user(a);
685 }
686
687 bool playlist_manager::remove_playlist_user(size_t which) {
688 if (this->get_playlist_count() == 1) {
689 // FIX ME implement toast
690 #ifdef _WIN32
691 MessageBeep(0);
692 #endif
693 return false;
694 }
695
696 if (!this->remove_playlist_switch(which)) {
697 // FIX ME implement toast
698 #ifdef _WIN32
699 MessageBeep(0);
700 #endif
701 return false;
702 }
703 return true;
704 }
705
706 bool playlist_manager::remove_playlist_switch(t_size idx)
707 {
708 bool need_switch = get_active_playlist() == idx;
709 if (remove_playlist(idx))
710 {
711 if (need_switch)
712 {
713 t_size total = get_playlist_count();
714 if (total > 0)
715 {
716 if (idx >= total) idx = total-1;
717 set_active_playlist(idx);
718 }
719 }
720 return true;
721 }
722 else return false;
723 }
724
725
726
727 bool t_playback_queue_item::operator==(const t_playback_queue_item & p_item) const
728 {
729 return m_handle == p_item.m_handle && m_playlist == p_item.m_playlist && m_item == p_item.m_item;
730 }
731
732 bool t_playback_queue_item::operator!=(const t_playback_queue_item & p_item) const
733 {
734 return m_handle != p_item.m_handle || m_playlist != p_item.m_playlist || m_item != p_item.m_item;
735 }
736
737
738
739 bool playlist_manager::activeplaylist_execute_default_action(t_size p_item) {
740 t_size idx = get_active_playlist();
741 if (idx == pfc_infinite) return false;
742 else return playlist_execute_default_action(idx,p_item);
743 }
744
745 namespace {
746 class completion_notify_dfd : public completion_notify {
747 public:
748 completion_notify_dfd(const pfc::list_base_const_t<metadb_handle_ptr> & p_data,service_ptr_t<process_locations_notify> p_notify) : m_data(p_data), m_notify(p_notify) {}
749 void on_completion(unsigned p_code) {
750 switch(p_code) {
751 case metadb_io::load_info_aborted:
752 m_notify->on_aborted();
753 break;
754 default:
755 m_notify->on_completion(m_data);
756 break;
757 }
758 }
759 private:
760 metadb_handle_list m_data;
761 service_ptr_t<process_locations_notify> m_notify;
762 };
763 };
764
765 void dropped_files_data_impl::to_handles_async_ex(t_uint32 p_op_flags,fb2k::hwnd_t p_parentwnd,service_ptr_t<process_locations_notify> p_notify) {
766 if (m_is_paths) {
767 playlist_incoming_item_filter_v2::get()->process_locations_async(
768 m_paths,
769 p_op_flags,
770 NULL,
771 NULL,
772 p_parentwnd,
773 p_notify);
774 } else {
775 t_uint32 flags = 0;
776 if (p_op_flags & playlist_incoming_item_filter_v2::op_flag_background) flags |= metadb_io_v2::op_flag_background;
777 if (p_op_flags & playlist_incoming_item_filter_v2::op_flag_delay_ui) flags |= metadb_io_v2::op_flag_delay_ui;
778 metadb_io_v2::get()->load_info_async(m_handles,metadb_io::load_info_default,p_parentwnd,flags,new service_impl_t<completion_notify_dfd>(m_handles,p_notify));
779 }
780 }
781 void dropped_files_data_impl::to_handles_async(bool p_filter,fb2k::hwnd_t p_parentwnd,service_ptr_t<process_locations_notify> p_notify) {
782 to_handles_async_ex(p_filter ? 0 : playlist_incoming_item_filter_v2::op_flag_no_filter,p_parentwnd,p_notify);
783 }
784
785 bool dropped_files_data_impl::to_handles(pfc::list_base_t<metadb_handle_ptr> & p_out,bool p_filter,fb2k::hwnd_t p_parentwnd) {
786 if (m_is_paths) {
787 return playlist_incoming_item_filter::get()->process_locations(m_paths,p_out,p_filter,NULL,NULL,p_parentwnd);
788 } else {
789 if (metadb_io::get()->load_info_multi(m_handles,metadb_io::load_info_default,p_parentwnd,true) == metadb_io::load_info_aborted) return false;
790 p_out = m_handles;
791 return true;
792 }
793 }
794
795 void playlist_manager::playlist_activate_delta(int p_delta) {
796 const t_size total = get_playlist_count();
797 if (total > 0) {
798 t_size active = get_active_playlist();
799
800 //clip p_delta to -(total-1)...(total-1) range
801 if (p_delta < 0) {
802 p_delta = - ( (-p_delta) % (t_ssize)total );
803 } else {
804 p_delta = p_delta % total;
805 }
806 if (p_delta != 0) {
807 if (active == pfc_infinite) {
808 //special case when no playlist is active
809 if (p_delta > 0) {
810 active = (t_size)(p_delta - 1);
811 } else {
812 active = (total + p_delta);//p_delta is negative
813 }
814 } else {
815 active = (t_size) (active + total + p_delta) % total;
816 }
817 set_active_playlist(active % total);
818 }
819 }
820 }
821 namespace {
822 class enum_items_callback_get_selected_count : public playlist_manager::enum_items_callback {
823 public:
824 enum_items_callback_get_selected_count() : m_found() {}
825 t_size get_count() const {return m_found;}
826 bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) override {
827 (void)p_index; (void)p_location;
828 if (b_selected) m_found++;
829 return true;
830 }
831 private:
832 t_size m_found;
833 };
834 }
835 t_size playlist_manager::playlist_get_selected_count(t_size p_playlist,bit_array const & p_mask) {
836 enum_items_callback_get_selected_count callback;
837 playlist_enum_items(p_playlist,callback,p_mask);
838 return callback.get_count();
839 }
840
841 namespace {
842 class enum_items_callback_find_item : public playlist_manager::enum_items_callback {
843 public:
844 enum_items_callback_find_item(metadb_handle_ptr p_lookingFor) : m_lookingFor(p_lookingFor) {}
845 t_size result() const {return m_result;}
846 bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) override {
847 (void)b_selected;
848 if (p_location == m_lookingFor) {
849 m_result = p_index;
850 return false;
851 } else {
852 return true;
853 }
854 }
855 private:
856 metadb_handle_ptr m_lookingFor;
857 size_t m_result = SIZE_MAX;
858 };
859 class enum_items_callback_find_item_selected : public playlist_manager::enum_items_callback {
860 public:
861 enum_items_callback_find_item_selected(metadb_handle_ptr p_lookingFor) : m_lookingFor(p_lookingFor) {}
862 t_size result() const {return m_result;}
863 bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) {
864 if (b_selected && p_location == m_lookingFor) {
865 m_result = p_index;
866 return false;
867 } else {
868 return true;
869 }
870 }
871 private:
872 metadb_handle_ptr m_lookingFor;
873 size_t m_result = SIZE_MAX;
874 };
875 }
876
877 bool playlist_manager::playlist_find_item(t_size p_playlist,metadb_handle_ptr p_item,t_size & p_result) {
878 enum_items_callback_find_item callback(p_item);
879 playlist_enum_items(p_playlist,callback,pfc::bit_array_true());
880 t_size result = callback.result();
881 if (result == SIZE_MAX) return false;
882 p_result = result;
883 return true;
884 }
885 bool playlist_manager::playlist_find_item_selected(t_size p_playlist,metadb_handle_ptr p_item,t_size & p_result) {
886 enum_items_callback_find_item_selected callback(p_item);
887 playlist_enum_items(p_playlist,callback,pfc::bit_array_true());
888 t_size result = callback.result();
889 if (result == SIZE_MAX) return false;
890 p_result = result;
891 return true;
892 }
893 t_size playlist_manager::playlist_set_focus_by_handle(t_size p_playlist,metadb_handle_ptr p_item) {
894 t_size index;
895 if (!playlist_find_item(p_playlist,p_item,index)) index = SIZE_MAX;
896 playlist_set_focus_item(p_playlist,index);
897 return index;
898 }
899 bool playlist_manager::activeplaylist_find_item(metadb_handle_ptr p_item,t_size & p_result) {
900 t_size playlist = get_active_playlist();
901 if (playlist == SIZE_MAX) return false;
902 return playlist_find_item(playlist,p_item,p_result);
903 }
904 t_size playlist_manager::activeplaylist_set_focus_by_handle(metadb_handle_ptr p_item) {
905 t_size playlist = get_active_playlist();
906 if (playlist == SIZE_MAX) return SIZE_MAX;
907 return playlist_set_focus_by_handle(playlist,p_item);
908 }
909
910 #ifdef _WIN32
911 pfc::com_ptr_t<interface IDataObject> playlist_incoming_item_filter::create_dataobject_ex(metadb_handle_list_cref data) {
912 pfc::com_ptr_t<interface IDataObject> temp; temp.attach( create_dataobject(data) ); PFC_ASSERT( temp.is_valid() ); return temp;
913 }
914 #endif
915
916 void playlist_manager_v3::recycler_restore_by_id(t_uint32 id) {
917 t_size which = recycler_find_by_id(id);
918 if (which != ~0) recycler_restore(which);
919 }
920
921 t_size playlist_manager_v3::recycler_find_by_id(t_uint32 id) {
922 const t_size total = recycler_get_count();
923 for(t_size walk = 0; walk < total; ++walk) {
924 if (id == recycler_get_id(walk)) return walk;
925 }
926 return SIZE_MAX;
927 }
928
929
930
931 typedef pfc::map_t< const char*, metadb_handle_list, metadb::path_comparator > byPath_t;
932 static void rechapter_worker(playlist_manager* api, byPath_t const& byPath) {
933 if (byPath.get_count() == 0) return;
934 const size_t numPlaylists = api->get_playlist_count();
935 for (size_t walkPlaylist = 0; walkPlaylist < numPlaylists; ++walkPlaylist) {
936 if (!api->playlist_lock_is_present(walkPlaylist)) {
937 auto itemCount = [=] {
938 return api->playlist_get_item_count(walkPlaylist);
939 };
940 auto itemHandle = [=](size_t item) -> metadb_handle_ptr {
941 return api->playlist_get_item_handle(walkPlaylist, item);
942 };
943
944 for (size_t walkItem = 0; walkItem < itemCount(); ) {
945 auto item = itemHandle(walkItem);
946 auto itemPath = item->get_path();
947 auto match = byPath.find(itemPath);
948 if (match.is_valid() ) {
949 pfc::avltree_t<uint32_t> subsongs;
950 auto base = walkItem;
951 bool bSel = false;
952 for (++walkItem; walkItem < itemCount(); ++walkItem) {
953 auto handle = itemHandle(walkItem);
954 if (metadb::path_compare(itemPath, handle->get_path()) != 0) break;
955 if (!subsongs.add_item_check(handle->get_subsong_index())) break;
956
957 bSel = bSel || api->playlist_is_item_selected(walkPlaylist, walkItem);
958 }
959
960 const auto& newItems = match->m_value;
961 // REMOVE base ... walkItem range and insert newHandles at base
962 api->playlist_remove_items(walkPlaylist, pfc::bit_array_range(base, walkItem - base));
963 api->playlist_insert_items(walkPlaylist, base, newItems, pfc::bit_array_val(bSel));
964 walkItem = base + newItems.get_size();
965 }
966 else {
967 ++walkItem;
968 }
969 }
970 }
971 }
972 }
973
974 void playlist_manager::on_files_rechaptered( metadb_handle_list_cref newHandles ) {
975 pfc::map_t< const char*, metadb_handle_list, metadb::path_comparator > byPath;
976
977 const size_t total = newHandles.get_count();
978 for( size_t w = 0; w < total; ++w ) {
979 auto handle = newHandles[w];
980 byPath[ handle->get_path() ] += handle;
981 }
982
983 rechapter_worker(this, byPath);
984 }
985
986 void playlist_manager::on_file_rechaptered(const char* path, metadb_handle_list_cref newItems) {
987 // obsolete method
988 (void)path;
989 on_files_rechaptered(newItems);
990 }
991
992 namespace {
993 class process_locations_notify_lambda : public process_locations_notify {
994 public:
995 process_locations_notify::func_t f;
996 void on_completion(metadb_handle_list_cref p_items) override {
997 PFC_ASSERT(f != nullptr);
998 f(p_items);
999 }
1000 void on_aborted() override {}
1001 };
1002 }
1003 process_locations_notify::ptr process_locations_notify::create(func_t arg) {
1004 PFC_ASSERT(arg != nullptr);
1005 auto ret = fb2k::service_new< process_locations_notify_lambda >();
1006 ret->f = arg;
1007 return ret;
1008 }