/* * Copyright 2016 (c) Anna Schumaker. */ #include #include #include static struct playlist_callbacks *callbacks = NULL; static int __playlist_generic_find_sort(gconstpointer a, gconstpointer b) { return abs(GPOINTER_TO_INT(a)) - abs(GPOINTER_TO_INT(b)); } static int __playlist_generic_less_than(gconstpointer a, gconstpointer b, gpointer data) { struct track *lhs = (struct track *)a; struct track *rhs = (struct track *)b; GSList *cur = ((struct playlist *)data)->pl_sort; int res, field; while (cur) { field = GPOINTER_TO_INT(cur->data); res = track_compare(lhs, rhs, abs(field)); if (res != 0) break; cur = g_slist_next(cur); }; return (field > 0) ? res : -res; } void playlist_generic_set_callbacks(struct playlist_callbacks *cb) { callbacks = cb; } void playlist_generic_init(struct playlist *playlist) { if (playlist) { g_queue_init(&playlist->pl_tracks); playlist->pl_sort = NULL; playlist->pl_current = NULL; playlist->pl_search = NULL; } } void playlist_generic_init_sorted(struct playlist *playlist) { if (playlist) { playlist_generic_init(playlist); playlist_generic_sort(playlist, COMPARE_ARTIST); playlist_generic_sort(playlist, COMPARE_YEAR); playlist_generic_sort(playlist, COMPARE_TRACK); } } void playlist_generic_deinit(struct playlist *playlist) { if (playlist) { playlist_generic_clear(playlist); playlist_clear_sort(playlist); g_strfreev(playlist->pl_search); playlist->pl_search = NULL; playlist->pl_length = 0; } } struct playlist *playlist_generic_alloc(gchar *name, enum playlist_type_t type, unsigned int id, struct playlist_ops *ops) { struct playlist *playlist = g_malloc(sizeof(struct playlist)); playlist->pl_name = name; playlist->pl_type = type; playlist->pl_id = id; playlist->pl_ops = ops; playlist_generic_init_sorted(playlist); if (callbacks) callbacks->pl_cb_alloc(playlist); return playlist; } void playlist_generic_free(struct playlist *playlist) { if (playlist) { playlist_generic_deinit(playlist); g_free(playlist); } } void playlist_generic_save(struct playlist *playlist, struct file *file, unsigned int flags) { playlist_iter it; GSList *sort; int field; if (!playlist) return; if (flags & PL_SAVE_ITER) file_writef(file, "%u ", playlist_current_index(playlist)); if (flags & PL_SAVE_FLAGS) { sort = playlist->pl_sort; file_writef(file, "%u ", playlist->pl_random ? PL_RANDOM : 0); file_writef(file, "%u", g_slist_length(sort)); while (sort) { field = GPOINTER_TO_INT(sort->data); file_writef(file, " %u %d", abs(field) - 1, field > 0); sort = g_slist_next(sort); } file_writef(file, "\n"); } if (flags & PL_SAVE_TRACKS) { file_writef(file, "%u", playlist_size(playlist)); playlist_for_each(playlist, it) file_writef(file, " %u", track_index(playlist_iter_track(it))); file_writef(file, "\n"); } } void playlist_generic_load(struct playlist *playlist, struct file *file, unsigned int flags) { unsigned int f, n, i, t, it = 0; int field, ascending; gchar *line; if (!playlist) return; if (flags & PL_SAVE_ITER) file_readf(file, "%u", &it); if (flags & PL_SAVE_FLAGS) { file_readf(file, "%u %u", &f, &n); playlist_clear_sort(playlist); for (i = 0; i < n; i++) { file_readf(file, "%u %d", &field, &ascending); field += 1; if (!ascending) field = -field; playlist->pl_sort = g_slist_append(playlist->pl_sort, GINT_TO_POINTER(field)); } playlist_generic_resort(playlist); if (file_readf(file, "%m\n", &line)) g_free(line); } if (flags & PL_SAVE_TRACKS) { file_readf(file, "%u ", &n); for (i = 0; i < n; i++) { file_readf(file, "%u", &t); playlist_generic_add(playlist, track_get(t)); } if (file_readf(file, "%m\n", &line)) g_free(line); } playlist_generic_set_random(playlist, f == PL_RANDOM); playlist_current_set(playlist, it); } bool playlist_generic_can_select(struct playlist *playlist) { return playlist_size(playlist) > 0; } void playlist_generic_clear(struct playlist *playlist) { unsigned int n; if (!playlist) return; n = playlist_size(playlist); g_queue_clear(&playlist->pl_tracks); playlist->pl_length = 0; playlist->pl_current = NULL; if (callbacks) callbacks->pl_cb_removed(playlist, NULL, n); } bool playlist_generic_add(struct playlist *playlist, struct track *track) { if (!playlist || !track || playlist_has(playlist, track)) return false; playlist->pl_length += track->tr_length; if (playlist->pl_sort) { g_queue_insert_sorted(&playlist->pl_tracks, track, __playlist_generic_less_than, playlist); } else g_queue_push_tail(&playlist->pl_tracks, track); if (callbacks) callbacks->pl_cb_added(playlist, track); return true; } bool playlist_generic_add_front(struct playlist *playlist, struct track *track) { if (!playlist || !track) return false; playlist->pl_length += track->tr_length; g_queue_push_head(&playlist->pl_tracks, track); if (callbacks) callbacks->pl_cb_added(playlist, track); return true; } bool playlist_generic_remove(struct playlist *playlist, struct track *track) { unsigned int count; if (!playlist || !track) return false; while (playlist_current_track(playlist) == track) playlist_current_previous(playlist); count = g_queue_remove_all(&playlist->pl_tracks, track); playlist->pl_length -= (count * track->tr_length); if (callbacks) callbacks->pl_cb_removed(playlist, track, count); return count > 0; } void playlist_generic_update(struct playlist *playlist, struct track *track) { if (playlist && callbacks) callbacks->pl_cb_updated(playlist, track); } void playlist_generic_set_random(struct playlist *playlist, bool enabled) { playlist->pl_random = enabled; } void playlist_generic_sort(struct playlist *playlist, enum compare_t field) { gpointer sort = GINT_TO_POINTER(field); GSList *found = g_slist_find_custom(playlist->pl_sort, sort, __playlist_generic_find_sort); if (found) found->data = GINT_TO_POINTER(-field); else playlist->pl_sort = g_slist_append(playlist->pl_sort, sort); playlist_generic_resort(playlist); } void playlist_generic_resort(struct playlist *playlist) { if (!playlist || !playlist->pl_sort) return; g_queue_sort(&playlist->pl_tracks, __playlist_generic_less_than, playlist); playlist_generic_update(playlist, NULL); } struct track *playlist_generic_next(struct playlist *playlist) { unsigned int pos, size = playlist_size(playlist); if (size == 0) return NULL; else if (playlist->pl_random) { pos = playlist_current_index(playlist); pos += g_random_int_range(1, size); playlist_current_set(playlist, pos % size); } else if (!playlist_current_next(playlist)) playlist_current_set(playlist, 0); return playlist_current_track(playlist); }