/* * Copyright 2013 (c) Anna Schumaker. */ #include #include #include #include #include #define O_MINUTES (60) #define O_HOURS (60 * O_MINUTES) #define O_DAYS (24 * O_HOURS) Queue :: Queue() : _cur(-1), _flags(0), _length(0) { } Queue :: Queue(unsigned int f) : _cur(-1), _flags(f & Q_FLAG_MASK), _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, id; f >> _flags >> n; _tracks.resize(n); for (unsigned int i = 0; i < n; i++) { f >> id; _tracks[i] = tagdb :: lookup(id); } } void Queue :: set_flag(queue_flags f) { _flags |= f; } void Queue :: unset_flag(queue_flags f) { _flags &= ~f; } bool Queue :: has_flag(queue_flags f) { return (_flags & f) == (unsigned int)f; } /* * 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 = _tracks[mid]; if (track_less_than(lhs, rhs, _sort_order)) begin = mid + 1; else { if (mid == begin) return begin; else end = mid - 1; } } lhs = _tracks[begin]; if (track_less_than(lhs, rhs, _sort_order)) return begin + 1; return begin; } unsigned int Queue :: _add_at(Track *track, unsigned int pos) { _tracks.insert(_tracks.begin() + pos, track); _length += track->length; get_callbacks()->on_queue_track_add(this, pos); if (!(_flags & Q_DISABLE_CHANGED_SIZE)) get_callbacks()->on_queue_changed(); return pos; } void Queue :: _del_at(Track *track, unsigned int pos) { _tracks.erase(_tracks.begin() + pos); _length -= track->length; get_callbacks()->on_queue_track_del(this, pos); if (!(_flags & Q_DISABLE_CHANGED_SIZE)) get_callbacks()->on_queue_changed(); } unsigned int Queue :: add(Track *track) { unsigned int id = _tracks.size(); if (_sort_order.size() > 0) id = find_sorted_id(track); return _add_at(track, id); } void Queue :: del(Track *track) { for (unsigned int i = 0; i < _tracks.size(); i++) { if (_tracks[i] == track) _del_at(track, i); } } void Queue :: del(unsigned int id) { _del_at(_tracks[id], id); } void Queue :: updated(Track *track) { for (unsigned int i = 0; i < _tracks.size(); i++) { if (_tracks[i] == track) get_callbacks()->on_queue_track_changed(this, i); } } Track *Queue :: next() { Track *res; if (_tracks.size() == 0) return NULL; else if (_tracks.size() == 1) _cur = 0; else if (_flags & Q_RANDOM) _cur += random(1, _tracks.size() / 2); else _cur++; _cur %= _tracks.size(); res = _tracks[_cur]; if (!(_flags & Q_REPEAT)) { del(_cur); _cur--; } return res; } unsigned int Queue :: size() { return _tracks.size(); } const std::string Queue :: size_str() { std::stringstream ss; ss << size(); return ss.str(); } const std::string Queue :: length_str() { std::stringstream ss; unsigned int factor[4] = { O_DAYS, O_HOURS, O_MINUTES, 1 }; std::string fields[4] = { "day", "hour", "minute", "second" }; unsigned int len = _length; for (unsigned int i = 0; i < 4; i++) { unsigned int dur = len / factor[i]; len -= dur * factor[i]; if (dur > 0) { ss << dur << " " << fields[i]; if (dur > 1) ss << "s"; if (len > 0) ss << ", "; } } return ss.str(); } /* Sorting function */ class SortTracks { private: std::list fields; public: SortTracks(std::list f) : fields(f) {} bool operator()(Track *a, Track *b) { Track *lhs = a; Track *rhs = 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_NO_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_NO_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; } Track *Queue :: operator[](unsigned int i) { return _tracks[i]; } 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 */