/* * Copyright 2013 (c) Anna Schumaker. */ #include #include #include #include #include #include #define O_MINUTES (60) #define O_HOURS (60 * O_MINUTES) #define O_DAYS (24 * O_HOURS) Queue :: Queue() : flags(0), cur(-1), length(0) { } Queue :: Queue(queue_flags f) : flags(f), cur(-1), length(0) { } Queue :: ~Queue() { } void Queue :: write(File &f) { f << flags << " " << tracks.size(); for (unsigned int i = 0; i < tracks.size(); i++) f << " " << tracks[i]; } void Queue :: read(File &f) { unsigned int n; f >> flags >> n; tracks.resize(n); for (unsigned int i = 0; i < n; i++) f >> tracks[i]; } void Queue :: set_flag(queue_flags f) { flags |= f; get_callbacks()->on_queue_changed(); } void Queue :: unset_flag(queue_flags f) { flags &= ~f; get_callbacks()->on_queue_changed(); } const unsigned int Queue :: get_flags() { return flags; } unsigned int Queue :: get_length() { return length; } static inline void add_duration(std::stringstream &ss, unsigned int dur, unsigned int remaining, const std::string &field) { if (dur > 0) { ss << dur << " " << field; if (dur > 1) ss << "s"; if (remaining > 0) ss << ", "; } } std::string Queue :: get_length_str() { std::stringstream ss; unsigned int len = length; unsigned int days = len / O_DAYS; len -= days * O_DAYS; add_duration(ss, days, len, "day"); unsigned int hours = len / O_HOURS; len -= hours *O_HOURS; add_duration(ss, hours, len, "hour"); unsigned int mins = len / O_MINUTES; add_duration(ss, mins, len, "minute"); unsigned int secs = len - (mins * O_MINUTES); add_duration(ss, secs, 0, "second"); return ss.str(); } /* * std::string.compare() returns * 0: Strings are equal * < 0: a < b * > 0: a > b */ static inline int track_compare(Track *lhs, Track *rhs, sort_t field) { int ret = lhs->less_than(rhs, field); if (field == SORT_YEAR && ret == 0) ret = lhs->less_than(rhs, SORT_ALBUM); return ret; } static bool track_less_than(Track *lhs, Track *rhs, std::list &order) { std::list::iterator it; int res; for (it = order.begin(); it != order.end(); it++) { if (it->ascending == true) res = track_compare(lhs, rhs, it->field); else res = track_compare(rhs, lhs, it->field); if (res != 0) return res < 0; } return res; } unsigned int Queue :: find_sorted_id(Track *rhs) { Track *lhs; unsigned int begin = 0, end = (tracks.size() - 1), mid; if (tracks.size() == 0) return 0; while (end > begin) { mid = begin + ((end - begin) / 2); lhs = tagdb :: lookup(tracks[mid]); if (track_less_than(lhs, rhs, sort_order)) begin = mid + 1; else { if (mid == begin) return begin; else end = mid - 1; } } lhs = tagdb :: lookup(tracks[begin]); if (track_less_than(lhs, rhs, sort_order)) return begin + 1; return begin; } unsigned int Queue :: add(unsigned int track_id) { unsigned int id = tracks.size(); Track *track = tagdb :: lookup(track_id); if (sort_order.size() > 0) id = find_sorted_id(track); tracks.insert(tracks.begin() + id, track_id); length += track->length; get_callbacks()->on_queue_track_add(this, id); if (!(flags & Q_DISABLE_CHANGED_SIZE)) get_callbacks()->on_queue_changed(); return id; } unsigned int Queue :: add_front(unsigned int track_id) { Track *track; tracks.insert(tracks.begin(), track_id); track = tagdb :: lookup(track_id); length += track->length; get_callbacks()->on_queue_track_add(this, 0); if (!(flags & Q_DISABLE_CHANGED_SIZE)) get_callbacks()->on_queue_changed(); return 0; } void Queue :: del(unsigned int plist_id) { Track *track; unsigned int track_id = tracks[plist_id]; tracks.erase(tracks.begin() + plist_id); track = tagdb :: lookup(track_id); length -= track->length; get_callbacks()->on_queue_track_del(this, plist_id); if (!(flags & Q_DISABLE_CHANGED_SIZE)) get_callbacks()->on_queue_changed(); } void Queue :: del_track(unsigned int track_id) { unsigned int i = 0; while (i < tracks.size()) { if (tracks[i] == track_id) del(i); else i++; } } void Queue :: track_updated(unsigned int track_id) { for (unsigned int i = 0; i < tracks.size(); i++) { if (tracks[i] == track_id) get_callbacks()->on_queue_track_changed(this, i); } } unsigned int Queue :: size() { return tracks.size(); } /* Sorting function */ class SortTracks { private: std::list fields; public: SortTracks(std::list f) : fields(f) {} bool operator()(unsigned int a, unsigned int b) { Track *lhs = tagdb :: lookup(a); Track *rhs = tagdb :: lookup(b); return track_less_than(lhs, rhs, fields); } }; void Queue :: _add_sort(sort_t field, bool ascending) { struct sort_info info; std::list::iterator it; /* Is field already in the sort_order? */ for (it = sort_order.begin(); it != sort_order.end(); it++) { if (it->field == field) { it->ascending = !it->ascending; return; } } info.field = field; info.ascending = ascending; sort_order.push_back(info); if (sort_order.size() >= 4) sort_order.erase(sort_order.begin()); } void Queue :: add_sort(sort_t field, bool ascending) { if (flags & Q_NEVER_SORT) return; _add_sort(field, ascending); std::stable_sort(tracks.begin(), tracks.end(), SortTracks(sort_order)); for (unsigned int i = 0; i < tracks.size(); i++) get_callbacks()->on_queue_track_changed(this, i); get_callbacks()->on_queue_changed(); } void Queue :: reset_sort(sort_t field, bool ascending) { if (flags & Q_NEVER_SORT) return; if (sort_order.front().field != field) sort_order.clear(); add_sort(field, ascending); } void Queue :: force_clear_sort() { sort_order.clear(); } std::list &Queue :: get_sort_order() { return sort_order; } unsigned int Queue :: operator[](unsigned int i) { return tracks[i]; } unsigned int Queue :: next() { unsigned int res; if (tracks.size() == 0) throw -E_EXIST; else if (tracks.size() == 1) cur = 0; else if (flags & Q_RANDOM) cur += rand() % (tracks.size() / 2) + 1; else cur++; if (cur >= tracks.size()) cur -= tracks.size(); res = tracks[cur]; if (!(flags & Q_REPEAT)) { del(cur); cur--; } return res; } void Queue :: set_cur(unsigned int c) { cur = c; } void Queue :: path_selected(unsigned int id) { cur = id; if (!(flags &Q_REPEAT)) { del(cur); cur--; } } #ifdef CONFIG_TEST void Queue :: reset() { tracks.clear(); set_cur(0); } #endif /* CONFIG_TEST */