ocarina/core/queue.cpp
Anna Schumaker c5598293d6 queue: Create a notifier class
This class will be used to push queue changes directly to the GUI.
Currently changes get mapped through the old callbacks system, which can
lead to several inefficiencies because the GUI has to look up each queue
structure in a list.

This patch implements a basic QNotifier class and provides a function
for setting a Queue's notifier.

Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
2015-04-03 12:16:40 -04:00

267 lines
5.1 KiB
C++

/**
* @file
* Copyright 2013 (c) Anna Schumaker.
*/
#include <core/callback.h>
#include <core/queue.h>
#include <core/random.h>
#include <core/string.h>
#include <algorithm>
#include <sstream>
static class DefaultNotifier : public QNotifier {
public:
DefaultNotifier() {};
} def_notify;
Queue :: Queue(unsigned int flags)
: _cur(-1), _flags(flags), _length(0), _notify(&def_notify)
{}
Queue :: Queue()
: _cur(-1), _flags(0), _length(0), _notify(&def_notify)
{}
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;
}
void Queue :: set_notifier(QNotifier *notify)
{
_notify = notify;
}
/*
* 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_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:
if (lhs->album()->year() - rhs->album()->year() != 0)
return lhs->album()->year() - rhs->album()->year();
case SORT_ALBUM:
return lhs->album()->compare(rhs->album());
}
return 0;
}
static bool track_less_than(Track *lhs, Track *rhs,
std::vector<struct sort_info> &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();
}
unsigned int Queue :: length()
{
return _length;
}
class SortTracks {
public:
std::vector<struct sort_info> fields;
SortTracks(std::vector<sort_info> 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--;
}
}