ocarina/lib/queue.cpp

333 lines
6.2 KiB
C++
Raw Normal View History

/*
* Copyright 2013 (c) Anna Schumaker.
*/
#include <callback.h>
#include <library.h>
#include <queue.h>
#include <stdlib.h>
#include <algorithm>
#include <sstream>
#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;
}
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<sort_info> &order)
{
std::list<sort_info>::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(Track *track)
{
unsigned int id = _tracks.size();
if (_sort_order.size() > 0)
id = find_sorted_id(track);
_tracks.insert(_tracks.begin() + id, track);
_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 = tagdb :: lookup(track_id);
_tracks.insert(_tracks.begin(), track);
_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]->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]->id == 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]->id == track_id)
get_callbacks()->on_queue_track_changed(this, i);
}
}
unsigned int Queue :: size()
{
return _tracks.size();
}
/* Sorting function */
class SortTracks {
private:
std::list<sort_info> fields;
public:
SortTracks(std::list<sort_info> 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<sort_info>::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<sort_info> &Queue :: get_sort_order()
{
return _sort_order;
}
Track *Queue :: operator[](unsigned int i)
{
return _tracks[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 += 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 */