/** * @file * 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(unsigned int flags) : _cur(-1), _flags(flags), _length(0) {} Queue :: Queue() : _cur(-1), _flags(0), _length(0) {} Queue :: ~Queue() {} void Queue :: write(File &file) { file << _flags << " " << _tracks.size(); for (unsigned int i = 0; i < _tracks.size(); i++) file << " " << _tracks[i]->index(); } void Queue :: read(File &file) { unsigned int n, id; file >> _flags >> n; _tracks.resize(n); for (unsigned int i = 0; i < n; i++) { file >> id; _tracks[i] = tags :: get_track(id); _length += _tracks[i]->length(); } } void Queue :: set_flag(queue_flags flag) { _flags |= flag; } void Queue :: unset_flag(queue_flags flag) { _flags &= ~flag; } bool Queue :: has_flag(queue_flags flag) { return (_flags & flag) == (unsigned int)flag; } /* * Returns: * 0: lhs == rhs * < 0: lhs < rhs, or rhs is empty * > 0: lhs > rhs, or lhs is empty */ static inline int track_compare(Track *lhs, Track *rhs, sort_t field) { switch (field) { case SORT_ARTIST: return lhs->artist()->compare(rhs->artist()); case SORT_ALBUM: return lhs->album()->compare(rhs->album()); case SORT_COUNT: return lhs->count() - rhs->count(); case SORT_GENRE: return lhs->genre()->compare(rhs->genre()); case SORT_LENGTH: return lhs->length() - rhs->length(); case SORT_PLAYED: return rhs->compare_date(rhs); case SORT_TITLE: return lhs->compare(rhs); case SORT_TRACK: return lhs->track() - rhs->track(); case SORT_YEAR: return lhs->album()->year() - rhs->album()->year(); } return 0; } static bool track_less_than(Track *lhs, Track *rhs, std::vector &order) { int res; for (unsigned int i = 0; i < order.size(); i++) { if (order[i].ascending == true) res = track_compare(lhs, rhs, order[i].field); else res = track_compare(rhs, lhs, order[i].field); if (res == 0) continue; break; } return res < 0; } 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); return pos; } 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(unsigned int index) { _length -= _tracks[index]->length(); _tracks.erase(_tracks.begin() + index); get_callbacks()->on_queue_track_del(this, index); } void Queue :: del(Track *track) { for (unsigned int i = 0; i < _tracks.size(); i++) { while ((i < _tracks.size()) && (_tracks[i] == track)) del(i); } } 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(); } class SortTracks { public: std::vector fields; SortTracks(std::vector f) : fields(f) {} bool operator()(Track *lhs, Track *rhs) { return track_less_than(lhs, rhs, fields); } }; void Queue :: sort(sort_t field, bool reset) { bool found = false; struct sort_info info = { field, true }; if (_flags & Q_NO_SORT) return; if (reset) _sort_order.clear(); for (unsigned int i = 0; i < _sort_order.size(); i++) { if (_sort_order[i].field == info.field) { _sort_order[i].ascending = !_sort_order[i].ascending; found = true; break; } } if (!found) _sort_order.push_back(info); 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); } Track *Queue :: operator[](unsigned int index) { return _tracks[index]; } void Queue :: track_selected(unsigned int index) { _cur = index; if (has_flag(Q_REPEAT) == false) { del(_cur); _cur--; } }