Compare commits
258 Commits
Ocarina-6.
...
next
Author | SHA1 | Date |
---|---|---|
Anna Schumaker | 45d2422be3 | |
Anna Schumaker | 54138d8814 | |
Anna Schumaker | 6ab3cff28f | |
Anna Schumaker | 0972e027ed | |
Anna Schumaker | 77efa0c631 | |
Anna Schumaker | a9970c455f | |
Anna Schumaker | fc5e6eb043 | |
Anna Schumaker | a41652ab28 | |
Anna Schumaker | eca857cb3b | |
Anna Schumaker | d7fb67ed51 | |
Anna Schumaker | 7df129d533 | |
Anna Schumaker | a4cdac7f22 | |
Anna Schumaker | d5b0752497 | |
Anna Schumaker | 5fb46dc663 | |
Anna Schumaker | d149289e00 | |
Anna Schumaker | f167f968ba | |
Anna Schumaker | 94f3a7f387 | |
Anna Schumaker | 60e6e2a9eb | |
Anna Schumaker | 1836104f40 | |
Anna Schumaker | cd7364300e | |
Anna Schumaker | 994234caf2 | |
Anna Schumaker | af5bafb03e | |
Anna Schumaker | fd68cdf70a | |
Anna Schumaker | 8a2c631a9b | |
Anna Schumaker | e6ab06cf2b | |
Anna Schumaker | e6fb772cad | |
Anna Schumaker | cce8666140 | |
Anna Schumaker | edcba6a353 | |
Anna Schumaker | 59506d45e7 | |
Anna Schumaker | e1f13a7ef4 | |
Anna Schumaker | 111fcd4e72 | |
Anna Schumaker | c5494811f4 | |
Anna Schumaker | 07f832ad26 | |
Anna Schumaker | 35d53855f5 | |
Anna Schumaker | a848d5d03c | |
Anna Schumaker | 0aaafcb5f7 | |
Anna Schumaker | 8f13765b08 | |
Anna Schumaker | 84a1022bdf | |
Anna Schumaker | 659aaff6a1 | |
Anna Schumaker | 3736b6cf3b | |
Anna Schumaker | 3fdf89c75e | |
Anna Schumaker | 22854b2f25 | |
Anna Schumaker | b6d45e666e | |
Anna Schumaker | 842547d735 | |
Anna Schumaker | 198fbf7f9b | |
Anna Schumaker | 82280edfa2 | |
Anna Schumaker | 48f79bdb49 | |
Anna Schumaker | d7d553b80f | |
Anna Schumaker | a2854ef31e | |
Anna Schumaker | d96e8ca1ca | |
Anna Schumaker | 1940a31a77 | |
Anna Schumaker | 07196a7cc8 | |
Anna Schumaker | 64e27c1221 | |
Anna Schumaker | 31cda0eebd | |
Anna Schumaker | 9b9be4e322 | |
Anna Schumaker | 1374a025e1 | |
Anna Schumaker | 21e1796b14 | |
Anna Schumaker | b4347d5a34 | |
Anna Schumaker | 386514ac5c | |
Anna Schumaker | 7558a32940 | |
Anna Schumaker | a8f94e9443 | |
Anna Schumaker | cc464ed198 | |
Anna Schumaker | f9a573b6a3 | |
Anna Schumaker | e7ceed9b5d | |
Anna Schumaker | 497ed57057 | |
Anna Schumaker | e6f34d34f0 | |
Anna Schumaker | 4986bdad13 | |
Anna Schumaker | fc6e3ff464 | |
Anna Schumaker | c03530f318 | |
Anna Schumaker | 0f2e30589d | |
Anna Schumaker | 76e400e156 | |
Anna Schumaker | 9c6a9f7759 | |
Anna Schumaker | 1182e55df9 | |
Anna Schumaker | b558a9043c | |
Anna Schumaker | d95c693db2 | |
Anna Schumaker | 36349e9890 | |
Anna Schumaker | a7280356c8 | |
Anna Schumaker | 8b17962b4e | |
Anna Schumaker | 7e303fa2b1 | |
Anna Schumaker | 79accb5bb0 | |
Anna Schumaker | 8614aa37cd | |
Anna Schumaker | 4990d68711 | |
Anna Schumaker | fac383e9fc | |
Anna Schumaker | 466d9ce291 | |
Anna Schumaker | 3f9372051f | |
Anna Schumaker | 2cfccb8177 | |
Anna Schumaker | e773ae6f82 | |
Anna Schumaker | 042cddb65b | |
Anna Schumaker | 2a95031ee7 | |
Anna Schumaker | df21aa1299 | |
Anna Schumaker | 432d3e5d62 | |
Anna Schumaker | a15ad67029 | |
Anna Schumaker | 5247bf2de0 | |
Anna Schumaker | d3df9a69f2 | |
Anna Schumaker | f46ef37630 | |
Anna Schumaker | 76ebfaa6d4 | |
Anna Schumaker | cc5f65bf82 | |
Anna Schumaker | bb40ef479f | |
Anna Schumaker | d1c682501f | |
Anna Schumaker | 448b4a16f4 | |
Anna Schumaker | 3286b61dcf | |
Anna Schumaker | 7f54562b71 | |
Anna Schumaker | cdbf8b1736 | |
Anna Schumaker | 1dd0b7c2aa | |
Anna Schumaker | a808cac04c | |
Anna Schumaker | 1c6305e24e | |
Anna Schumaker | 8bf5aefd1a | |
Anna Schumaker | 03e7346900 | |
Anna Schumaker | 18f1bfe801 | |
Anna Schumaker | f25bdab367 | |
Anna Schumaker | 0c197c10f9 | |
Anna Schumaker | fda29aaf13 | |
Anna Schumaker | 2fb27178ee | |
Anna Schumaker | efbbc4ceff | |
Anna Schumaker | 92bb742f8f | |
Anna Schumaker | 6dbc574954 | |
Anna Schumaker | 859ff8656f | |
Anna Schumaker | a45c7d6889 | |
Anna Schumaker | ca6c5293c6 | |
Anna Schumaker | 287c6e0e9c | |
Anna Schumaker | 1d09e967d0 | |
Anna Schumaker | f670a3796b | |
Anna Schumaker | bd8df2a169 | |
Anna Schumaker | 07bf09c2ad | |
Anna Schumaker | e74c1c053c | |
Anna Schumaker | 793a8a5817 | |
Anna Schumaker | d2335f5c6e | |
Anna Schumaker | 37d95656e9 | |
Anna Schumaker | a87373f335 | |
Anna Schumaker | 93cb7145e6 | |
Anna Schumaker | ed095eb987 | |
Anna Schumaker | ee8825745b | |
Anna Schumaker | 4ad9b39398 | |
Anna Schumaker | ae604ab4a8 | |
Anna Schumaker | 2a8288e1b3 | |
Anna Schumaker | 8af9148606 | |
Anna Schumaker | 8a5163fb68 | |
Anna Schumaker | 8220dd9932 | |
Anna Schumaker | 0754f10883 | |
Anna Schumaker | a33decf549 | |
Anna Schumaker | 9fa5f0b0db | |
Anna Schumaker | b17585237a | |
Anna Schumaker | 7655b0cae8 | |
Anna Schumaker | 1fa31c51a6 | |
Anna Schumaker | 72d0f9a248 | |
Anna Schumaker | b5c1af263a | |
Anna Schumaker | 5c215df0bf | |
Anna Schumaker | 73825dd916 | |
Anna Schumaker | 09e358b96b | |
Anna Schumaker | bef0c70e5a | |
Anna Schumaker | 61e3137131 | |
Anna Schumaker | 0c1147513e | |
Anna Schumaker | 0e523ed279 | |
Anna Schumaker | 3364d4733e | |
Anna Schumaker | 1b9cdf5a5c | |
Anna Schumaker | 066027ecb6 | |
Anna Schumaker | ca5f0701e9 | |
Anna Schumaker | 846f7df9c1 | |
Anna Schumaker | 4d68ce8ce6 | |
Anna Schumaker | 249de0da21 | |
Anna Schumaker | 58cd43c330 | |
Anna Schumaker | 9b4a1785a1 | |
Anna Schumaker | c59b097638 | |
Anna Schumaker | 7e3e4194f3 | |
Anna Schumaker | 186367afe0 | |
Anna Schumaker | 62e494f2af | |
Anna Schumaker | 15ceda1194 | |
Anna Schumaker | 638caeaa91 | |
Anna Schumaker | a7565340d2 | |
Anna Schumaker | 0b231119d2 | |
Anna Schumaker | fc1e917aee | |
Anna Schumaker | bc1c462d36 | |
Anna Schumaker | 8fd4e4c637 | |
Anna Schumaker | 96e5749e7f | |
Anna Schumaker | 2a845feb38 | |
Anna Schumaker | f8c0668e5e | |
Anna Schumaker | 07d735eeee | |
Anna Schumaker | 943ab02aa5 | |
Anna Schumaker | 869e83b7bd | |
Anna Schumaker | 88bf71ac22 | |
Anna Schumaker | 3bf99b12eb | |
Anna Schumaker | e660e3f0b2 | |
Anna Schumaker | 3ae5e0f535 | |
Anna Schumaker | 39bc7492d4 | |
Anna Schumaker | e7d2fa5c4d | |
Anna Schumaker | 155b9c3ee6 | |
Anna Schumaker | 7fbef057bf | |
Anna Schumaker | d373c55320 | |
Anna Schumaker | f147c30c30 | |
Anna Schumaker | 1e6ab2e23c | |
Anna Schumaker | 83a21863b9 | |
Anna Schumaker | e876f8125f | |
Anna Schumaker | dc53ae271b | |
Anna Schumaker | c9e9e3a340 | |
Anna Schumaker | d818688bfd | |
Anna Schumaker | cebf2069cb | |
Anna Schumaker | 36f399ecb7 | |
Anna Schumaker | 83db8e4ae7 | |
Anna Schumaker | ad3e56250e | |
Anna Schumaker | bb673ddb62 | |
Anna Schumaker | 4fee5f85f0 | |
Anna Schumaker | ab47a7ac88 | |
Anna Schumaker | e550638823 | |
Anna Schumaker | 3e17b7bc1f | |
Anna Schumaker | b4e2770223 | |
Anna Schumaker | df2236db9f | |
Anna Schumaker | 1291a0d139 | |
Anna Schumaker | b3efd9d84d | |
Anna Schumaker | 828f861d9a | |
Anna Schumaker | 3562e164b0 | |
Anna Schumaker | a152ed689f | |
Anna Schumaker | a2113dc378 | |
Anna Schumaker | 9885c60bff | |
Anna Schumaker | 831a5379e5 | |
Anna Schumaker | f3f8ad91c6 | |
Anna Schumaker | 4fc8c72ea8 | |
Anna Schumaker | 18e76a7dca | |
Anna Schumaker | bbac5e23be | |
Anna Schumaker | c1c197acb5 | |
Anna Schumaker | 4c3405e874 | |
Anna Schumaker | b3074979f7 | |
Anna Schumaker | fd84222c2b | |
Anna Schumaker | 38cd2f761d | |
Anna Schumaker | a36fd137d5 | |
Anna Schumaker | a4049f8d01 | |
Anna Schumaker | 9b04ebcd71 | |
Anna Schumaker | d3f505465c | |
Anna Schumaker | 1d3438932e | |
Anna Schumaker | fa96def899 | |
Anna Schumaker | 2431ad104e | |
Anna Schumaker | 1c386809d0 | |
Anna Schumaker | dbc1df154a | |
Anna Schumaker | 730395aeff | |
Anna Schumaker | 9e37062920 | |
Anna Schumaker | df43010766 | |
Anna Schumaker | c49b77a24a | |
Anna Schumaker | 3fc19275f3 | |
Anna Schumaker | ac3c316d9a | |
Anna Schumaker | 32d712c213 | |
Anna Schumaker | 39d87f98f8 | |
Anna Schumaker | b9916706f1 | |
Anna Schumaker | ad29c520d4 | |
Anna Schumaker | f550d45811 | |
Anna Schumaker | 6325779062 | |
Anna Schumaker | 27a1e2e12a | |
Anna Schumaker | 619ea0b559 | |
Anna Schumaker | 010969c7b3 | |
Anna Schumaker | 82da46365f | |
Anna Schumaker | e522475d38 | |
Anna Schumaker | cdbe96c575 | |
Anna Schumaker | 453f176d63 | |
Anna Schumaker | 3383f9e32a | |
Anna Schumaker | f9238c34e4 | |
Anna Schumaker | 90b80fc8a7 | |
Anna Schumaker | 133efc0515 | |
Anna Schumaker | 85bb67feed | |
Anna Schumaker | e7b4973a50 | |
Anna Schumaker | 7d0dbcbdc7 |
|
@ -10,9 +10,10 @@ option(CONFIG_TESTING_ARTWORK "Enable album artwork fetching tests" ON)
|
|||
# Configure settings
|
||||
set(CONFIG_MAJOR 6)
|
||||
set(CONFIG_MINOR 5)
|
||||
set(CONFIG_RC OFF)
|
||||
set(CONFIG_MICRO 10)
|
||||
set(CONFIG_RC ON)
|
||||
|
||||
set(CONFIG_VERSION "${CONFIG_MAJOR}.${CONFIG_MINOR}")
|
||||
set(CONFIG_VERSION "${CONFIG_MAJOR}.${CONFIG_MINOR}.${CONFIG_MICRO}")
|
||||
|
||||
if (CONFIG_RC)
|
||||
set(CONFIG_VERSION "${CONFIG_VERSION}-rc")
|
||||
|
|
4
PKGBUILD
4
PKGBUILD
|
@ -1,12 +1,12 @@
|
|||
# Maintainer: Anna Schumaker <anna@nowheycreamery.com>
|
||||
pkgname=ocarina
|
||||
pkgver=6.5
|
||||
pkgver=6.5.9
|
||||
pkgrel=1
|
||||
pkgdesc="A simple GTK+ and GStreamer based music player."
|
||||
url="http://www.nowheycreamery.com/"
|
||||
arch=('x86_64' 'i686' 'armv7h')
|
||||
license=('GPL2')
|
||||
depends=('gtkmm3>=3.16' 'gstreamer' 'gst-plugins-base' 'taglib' 'libmusicbrainz5' 'libcoverart')
|
||||
depends=('gtk3>=3.22' 'gstreamer' 'gst-plugins-base' 'taglib' 'libmusicbrainz5' 'libcoverart')
|
||||
optdepends=('gst-plugins-good' 'gst-plugins-bad' 'gst-plugins-ugly')
|
||||
makedepends=('cmake')
|
||||
conflicts=()
|
||||
|
|
265
core/audio.c
265
core/audio.c
|
@ -6,77 +6,117 @@
|
|||
#include <core/playlist.h>
|
||||
#include <core/settings.h>
|
||||
|
||||
#define LOAD_PLAYING (1 << 0) /* Begin playback after loading */
|
||||
#define LOAD_HISTORY (1 << 1) /* Add the track to the history */
|
||||
#define LOAD_DEFAULT (LOAD_PLAYING | LOAD_HISTORY)
|
||||
|
||||
static const char *SETTINGS_TRACK = "core.audio.cur";
|
||||
static const char *SETTINGS_VOLUME = "core.audio.volume";
|
||||
static struct file audio_file = FILE_INIT("cur_track", 0);
|
||||
static struct track *audio_track = NULL;
|
||||
static GstElement *audio_player = NULL;
|
||||
static struct audio_ops *audio_ops = NULL;
|
||||
static int audio_pause_count = -1;
|
||||
static guint audio_bus = 0;
|
||||
|
||||
static struct file audio_file = FILE_INIT_DATA("", "cur_track", 0);
|
||||
static struct track *audio_track = NULL;
|
||||
static int audio_pause_count = -1;
|
||||
|
||||
static GstElement *audio_pipeline = NULL;
|
||||
static GstElement *audio_source = NULL;
|
||||
static GstElement *audio_decoder = NULL;
|
||||
static GstElement *audio_converter = NULL;
|
||||
static GstElement *audio_volume = NULL;
|
||||
static GstElement *audio_sink = NULL;
|
||||
static guint audio_bus_id = 0;
|
||||
|
||||
static struct audio_callbacks *audio_cb = NULL;
|
||||
|
||||
|
||||
static bool __audio_change_state(GstState state)
|
||||
{
|
||||
GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE;
|
||||
|
||||
if (audio_cur_state() != state)
|
||||
ret = gst_element_set_state(audio_player, state);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
if (audio_cur_state() == state)
|
||||
return false;
|
||||
|
||||
audio_ops->on_state_change(state);
|
||||
return true;
|
||||
return gst_element_set_state(audio_pipeline, state) != GST_STATE_CHANGE_FAILURE;
|
||||
}
|
||||
|
||||
/* Load a track, but don't add it to the history. */
|
||||
static struct track *__audio_load_basic(struct track *track, GstState state)
|
||||
static struct track *__audio_load(struct track *track, unsigned int flags)
|
||||
{
|
||||
struct track *prev = audio_track;
|
||||
gchar *path, *uri;
|
||||
gchar *path;
|
||||
|
||||
if (!track)
|
||||
return NULL;
|
||||
|
||||
path = track_path(track);
|
||||
uri = gst_filename_to_uri(path, NULL);
|
||||
audio_track = track;
|
||||
path = track_path(track);
|
||||
if (audio_cur_state() != GST_STATE_NULL)
|
||||
gst_element_set_state(audio_pipeline, GST_STATE_READY);
|
||||
g_object_set(G_OBJECT(audio_source), "location", path, NULL);
|
||||
gst_element_set_state(audio_pipeline, flags & LOAD_PLAYING ?
|
||||
GST_STATE_PLAYING : GST_STATE_PAUSED);
|
||||
|
||||
gst_element_set_state(audio_player, GST_STATE_NULL);
|
||||
g_object_set(G_OBJECT(audio_player), "uri", uri, NULL);
|
||||
audio_ops->on_load(track);
|
||||
__audio_change_state(state);
|
||||
playlist_played(prev);
|
||||
if (prev && TRACK_IS_EXTERNAL(prev))
|
||||
track_free_external(prev);
|
||||
|
||||
queue_updated(playlist_get_queue(PL_SYSTEM, "Queued Tracks"), prev);
|
||||
queue_updated(playlist_get_queue(PL_SYSTEM, "Collection"), prev);
|
||||
queue_updated(playlist_get_queue(PL_SYSTEM, "Collection"), audio_track);
|
||||
playlist_selected(track);
|
||||
if (flags & LOAD_HISTORY && !TRACK_IS_EXTERNAL(track))
|
||||
playlist_add(playlist_lookup(PL_SYSTEM, "History"), track);
|
||||
if (audio_cb)
|
||||
audio_cb->audio_cb_load(track);
|
||||
|
||||
audio_save();
|
||||
g_free(uri);
|
||||
g_free(path);
|
||||
return track;
|
||||
}
|
||||
|
||||
static struct track *__audio_load(struct track *track, GstState state)
|
||||
static void __audio_pad_added(GstElement *element, GstPad *pad, gpointer data)
|
||||
{
|
||||
if (__audio_load_basic(track, state))
|
||||
playlist_add(PL_SYSTEM, "History", track);
|
||||
return track;
|
||||
}
|
||||
GstPad *sink = gst_element_get_static_pad(audio_decoder, "sink");
|
||||
|
||||
static struct track *__audio_next(GstState state)
|
||||
{
|
||||
return __audio_load(playlist_next(), state);
|
||||
gst_element_link(element, audio_converter);
|
||||
gst_pad_link(pad, sink);
|
||||
gst_object_unref(sink);
|
||||
}
|
||||
|
||||
static gboolean __audio_message(GstBus *bus, GstMessage *message, gpointer data)
|
||||
{
|
||||
GstObject *source = GST_OBJECT(GST_MESSAGE_SRC(message));
|
||||
gchar *debug = NULL;
|
||||
GError *error = NULL;
|
||||
GstState old, state, next;
|
||||
unsigned int load_flags = LOAD_DEFAULT;
|
||||
|
||||
switch (GST_MESSAGE_TYPE(message)) {
|
||||
case GST_MESSAGE_ERROR:
|
||||
audio_error(message);
|
||||
gst_message_parse_error(message, &error, &debug);
|
||||
g_printerr("ERROR from element %s: %s\n",
|
||||
GST_OBJECT_NAME(source), error->message);
|
||||
g_printerr("DEBUG details: %s\n", debug ? debug : "none");
|
||||
g_error_free(error);
|
||||
g_free(debug);
|
||||
|
||||
if (audio_cur_state() != GST_STATE_PLAYING)
|
||||
load_flags = LOAD_HISTORY;
|
||||
__audio_load(playlist_next(), load_flags);
|
||||
break;
|
||||
|
||||
case GST_MESSAGE_EOS:
|
||||
audio_eos();
|
||||
track_played(audio_track);
|
||||
if (audio_pause_count >= 0) {
|
||||
audio_pause_after(audio_pause_count - 1);
|
||||
if (audio_pause_count == -1)
|
||||
load_flags = LOAD_HISTORY;
|
||||
}
|
||||
__audio_load(playlist_next(), load_flags);
|
||||
break;
|
||||
|
||||
case GST_MESSAGE_STATE_CHANGED:
|
||||
if (!audio_cb || source != GST_OBJECT(audio_pipeline))
|
||||
break;
|
||||
|
||||
gst_message_parse_state_changed(message, &old, &state, &next);
|
||||
if (state == GST_STATE_PLAYING || state == GST_STATE_PAUSED) {
|
||||
if (next == GST_STATE_VOID_PENDING)
|
||||
audio_cb->audio_cb_state_change(state);
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -90,29 +130,40 @@ static bool __audio_init_idle(void *data)
|
|||
|
||||
if (settings_has(SETTINGS_TRACK)) {
|
||||
track = settings_get(SETTINGS_TRACK);
|
||||
__audio_load(track_get(track), GST_STATE_PAUSED);
|
||||
__audio_load(track_get(track), LOAD_HISTORY);
|
||||
} else if (file_open(&audio_file, OPEN_READ)) {
|
||||
file_readf(&audio_file, "%u", &track);
|
||||
track = file_readu(&audio_file);
|
||||
file_close(&audio_file);
|
||||
file_remove(&audio_file);
|
||||
__audio_load(track_get(track), GST_STATE_PAUSED);
|
||||
__audio_load(track_get(track), LOAD_HISTORY);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void audio_init(int *argc, char ***argv, struct audio_ops *ops)
|
||||
void audio_init(int *argc, char ***argv, struct audio_callbacks *callbacks)
|
||||
{
|
||||
unsigned int volume = 100;
|
||||
GstBus *bus;
|
||||
|
||||
gst_init(argc, argv);
|
||||
audio_player = gst_element_factory_make("playbin", "ocarina_player");
|
||||
audio_ops = ops;
|
||||
audio_cb = callbacks;
|
||||
audio_pipeline = gst_pipeline_new("pipeline");
|
||||
audio_source = gst_element_factory_make("filesrc", "source");
|
||||
audio_decoder = gst_element_factory_make("decodebin", "decoder");
|
||||
audio_converter = gst_element_factory_make("audioconvert", "converter");
|
||||
audio_volume = gst_element_factory_make("volume", "volume");
|
||||
audio_sink = gst_element_factory_make("autoaudiosink", "sink");
|
||||
bus = gst_pipeline_get_bus(GST_PIPELINE(audio_pipeline));
|
||||
audio_bus_id = gst_bus_add_watch(bus, __audio_message, NULL);
|
||||
|
||||
bus = gst_pipeline_get_bus(GST_PIPELINE(audio_player));
|
||||
audio_bus = gst_bus_add_watch(bus, __audio_message, NULL);
|
||||
gst_bin_add_many(GST_BIN(audio_pipeline), audio_source, audio_decoder,
|
||||
audio_converter, audio_volume,
|
||||
audio_sink, NULL);
|
||||
gst_element_link(audio_source, audio_decoder);
|
||||
gst_element_link_many(audio_converter, audio_volume, audio_sink, NULL);
|
||||
g_signal_connect(audio_decoder, "pad-added", G_CALLBACK(__audio_pad_added), NULL);
|
||||
gst_object_unref(bus);
|
||||
|
||||
if (settings_has(SETTINGS_VOLUME))
|
||||
|
@ -124,19 +175,24 @@ void audio_init(int *argc, char ***argv, struct audio_ops *ops)
|
|||
|
||||
void audio_deinit()
|
||||
{
|
||||
gst_element_set_state(audio_player, GST_STATE_NULL);
|
||||
gst_object_unref(GST_ELEMENT(audio_player));
|
||||
g_source_remove(audio_bus);
|
||||
gst_element_set_state(audio_pipeline, GST_STATE_NULL);
|
||||
gst_object_unref(GST_ELEMENT(audio_pipeline));
|
||||
g_source_remove(audio_bus_id);
|
||||
|
||||
audio_player = NULL;
|
||||
audio_track = NULL;
|
||||
audio_pipeline = NULL;
|
||||
audio_source = NULL;
|
||||
audio_decoder = NULL;
|
||||
audio_converter = NULL;
|
||||
audio_volume = NULL;
|
||||
audio_sink = NULL;
|
||||
audio_track = NULL;
|
||||
|
||||
gst_deinit();
|
||||
}
|
||||
|
||||
void audio_save()
|
||||
{
|
||||
if (audio_track)
|
||||
if (audio_track && !TRACK_IS_EXTERNAL(audio_track))
|
||||
settings_set(SETTINGS_TRACK, track_index(audio_track));
|
||||
}
|
||||
|
||||
|
@ -144,7 +200,19 @@ bool audio_load(struct track *track)
|
|||
{
|
||||
if (track == audio_track)
|
||||
return false;
|
||||
return __audio_load(track, GST_STATE_PLAYING);
|
||||
return __audio_load(track, LOAD_DEFAULT) != NULL;
|
||||
}
|
||||
|
||||
bool audio_load_filepath(const gchar *filepath)
|
||||
{
|
||||
struct track *track;
|
||||
|
||||
if (!filepath)
|
||||
return false;
|
||||
track = track_lookup(filepath);
|
||||
if (!track)
|
||||
track = track_alloc_external(filepath);
|
||||
return audio_load(track);
|
||||
}
|
||||
|
||||
struct track *audio_cur_track()
|
||||
|
@ -155,8 +223,8 @@ struct track *audio_cur_track()
|
|||
GstState audio_cur_state()
|
||||
{
|
||||
GstState cur = GST_STATE_NULL;
|
||||
if (audio_player)
|
||||
gst_element_get_state(audio_player,
|
||||
if (audio_pipeline)
|
||||
gst_element_get_state(audio_pipeline,
|
||||
&cur, NULL,
|
||||
GST_CLOCK_TIME_NONE);
|
||||
return cur;
|
||||
|
@ -171,13 +239,13 @@ void audio_set_volume(unsigned int volume)
|
|||
vol = (gdouble)volume / 100;
|
||||
|
||||
settings_set(SETTINGS_VOLUME, volume);
|
||||
g_object_set(G_OBJECT(audio_player), "volume", vol, NULL);
|
||||
g_object_set(G_OBJECT(audio_volume), "volume", vol, NULL);
|
||||
}
|
||||
|
||||
unsigned int audio_get_volume()
|
||||
{
|
||||
gdouble volume;
|
||||
g_object_get(G_OBJECT(audio_player), "volume", &volume, NULL);
|
||||
g_object_get(G_OBJECT(audio_volume), "volume", &volume, NULL);
|
||||
return volume * 100;
|
||||
}
|
||||
|
||||
|
@ -199,7 +267,7 @@ bool audio_seek(gint64 offset)
|
|||
{
|
||||
if (!audio_track)
|
||||
return false;
|
||||
return gst_element_seek_simple(audio_player,
|
||||
return gst_element_seek_simple(audio_pipeline,
|
||||
GST_FORMAT_TIME,
|
||||
GST_SEEK_FLAG_FLUSH,
|
||||
offset);
|
||||
|
@ -208,17 +276,17 @@ bool audio_seek(gint64 offset)
|
|||
gint64 audio_position()
|
||||
{
|
||||
gint64 position;
|
||||
if (gst_element_query_position(audio_player,
|
||||
if (gst_element_query_position(audio_pipeline,
|
||||
GST_FORMAT_TIME,
|
||||
&position))
|
||||
return position;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t audio_duration()
|
||||
gint64 audio_duration()
|
||||
{
|
||||
gint64 duration;
|
||||
if (gst_element_query_duration(audio_player,
|
||||
if (gst_element_query_duration(audio_pipeline,
|
||||
GST_FORMAT_TIME,
|
||||
&duration))
|
||||
return duration;
|
||||
|
@ -229,65 +297,48 @@ int64_t audio_duration()
|
|||
|
||||
struct track *audio_next()
|
||||
{
|
||||
return __audio_next(GST_STATE_PLAYING);
|
||||
return __audio_load(playlist_next(), LOAD_DEFAULT);
|
||||
}
|
||||
|
||||
struct track *audio_prev()
|
||||
{
|
||||
return __audio_load_basic(playlist_prev(), GST_STATE_PLAYING);
|
||||
return __audio_load(playlist_prev(), LOAD_PLAYING);
|
||||
}
|
||||
|
||||
struct track *audio_eos()
|
||||
bool audio_pause_after(int n)
|
||||
{
|
||||
/* Mark current track as played */
|
||||
if (audio_track) {
|
||||
track_played(audio_track);
|
||||
playlist_update(PL_SYSTEM, "Unplayed");
|
||||
playlist_update(PL_SYSTEM, "Most Played");
|
||||
playlist_update(PL_SYSTEM, "Least Played");
|
||||
}
|
||||
|
||||
/* Check pause count and pick the next track */
|
||||
if (audio_pause_count >= 0) {
|
||||
audio_pause_after(audio_pause_count - 1);
|
||||
if (audio_pause_count == -1)
|
||||
return __audio_next(GST_STATE_PAUSED);
|
||||
}
|
||||
return __audio_next(GST_STATE_PLAYING);
|
||||
}
|
||||
|
||||
void audio_error(GstMessage *error)
|
||||
{
|
||||
gchar *path = NULL, *debug = NULL;
|
||||
GError *err = NULL;
|
||||
|
||||
if (audio_track)
|
||||
path = track_path(audio_track);
|
||||
if (error)
|
||||
gst_message_parse_error(error, &err, &debug);
|
||||
|
||||
g_print("Error: %s (%s)\n", err->message, path);
|
||||
if (debug)
|
||||
g_print("Debug details: %s\n", debug);
|
||||
|
||||
__audio_next(audio_cur_state());
|
||||
|
||||
g_error_free(err);
|
||||
g_free(debug);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
void audio_pause_after(int n)
|
||||
{
|
||||
if (n != audio_pause_count) {
|
||||
if (n >= -1 && n != audio_pause_count) {
|
||||
audio_pause_count = n;
|
||||
audio_ops->on_config_pause(audio_pause_count);
|
||||
if (audio_cb)
|
||||
audio_cb->audio_cb_config_pause(audio_pause_count);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int audio_get_pause_count(void)
|
||||
{
|
||||
return audio_pause_count;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TESTING
|
||||
GstElement *test_audio_player()
|
||||
void test_audio_eos()
|
||||
{
|
||||
return audio_player;
|
||||
GstMessage *message = gst_message_new_eos(GST_OBJECT(audio_pipeline));
|
||||
__audio_message(NULL, message, NULL);
|
||||
gst_message_unref(message);
|
||||
}
|
||||
|
||||
void test_audio_error(GError *error, gchar *debug)
|
||||
{
|
||||
GstMessage *message = gst_message_new_error(
|
||||
GST_OBJECT(audio_pipeline), error, debug);
|
||||
__audio_message(NULL, message, NULL);
|
||||
gst_message_unref(message);
|
||||
}
|
||||
|
||||
GstElement *test_audio_pipeline()
|
||||
{
|
||||
return audio_pipeline;
|
||||
}
|
||||
#endif /* CONFIG_TESTING */
|
||||
|
|
19
core/core.c
19
core/core.c
|
@ -1,13 +1,7 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/audio.h>
|
||||
#include <core/core.h>
|
||||
#include <core/idle.h>
|
||||
#include <core/playlist.h>
|
||||
#include <core/settings.h>
|
||||
#include <core/tags/tags.h>
|
||||
|
||||
|
||||
static bool core_defragment(void *data)
|
||||
{
|
||||
|
@ -18,17 +12,14 @@ static bool core_defragment(void *data)
|
|||
return true;
|
||||
}
|
||||
|
||||
void core_init(int *argc, char ***argv, struct core_init_data *init)
|
||||
void core_init(int *argc, char ***argv, struct playlist_callbacks *playlist_cb,
|
||||
struct audio_callbacks *audio_cb, enum idle_sync_t idle_sync)
|
||||
{
|
||||
#ifdef CONFIG_TESTING
|
||||
idle_init_sync();
|
||||
#else
|
||||
idle_init();
|
||||
#endif /* CONFIG_TESTING */
|
||||
idle_init(idle_sync);
|
||||
settings_init();
|
||||
tags_init();
|
||||
playlist_init(init->playlist_ops);
|
||||
audio_init(argc, argv, init->audio_ops);
|
||||
playlist_init(playlist_cb);
|
||||
audio_init(argc, argv, audio_cb);
|
||||
|
||||
idle_schedule(IDLE_SYNC, core_defragment, NULL);
|
||||
}
|
||||
|
|
|
@ -36,11 +36,9 @@ static struct db_entry *__dbe_next(const struct database *db, unsigned int index
|
|||
static struct db_entry *__dbe_read(struct database *db, unsigned int index)
|
||||
{
|
||||
struct db_entry *dbe = NULL;
|
||||
int valid;
|
||||
|
||||
file_readf(&db->db_file, "%d", &valid);
|
||||
if (valid)
|
||||
dbe = db->db_ops->dbe_read(&db->db_file);
|
||||
if (file_readd(&db->db_file))
|
||||
dbe = db->db_ops->dbe_read(&db->db_file, index);
|
||||
|
||||
g_ptr_array_index(db->db_entries, index) = dbe;
|
||||
return dbe;
|
||||
|
@ -76,7 +74,7 @@ void db_init(struct database *db, const char *filepath, bool autosave,
|
|||
db->db_autosave = autosave;
|
||||
db->db_entries = g_ptr_array_new();
|
||||
db->db_keys = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
file_init(&db->db_file, filepath, fmin);
|
||||
file_init_data(&db->db_file, "", filepath, fmin);
|
||||
}
|
||||
|
||||
void db_deinit(struct database *db)
|
||||
|
@ -118,7 +116,7 @@ void db_load(struct database *db)
|
|||
if (file_open(&db->db_file, OPEN_READ) == false)
|
||||
return;
|
||||
|
||||
file_readf(&db->db_file, "%u", &size);
|
||||
size = file_readu(&db->db_file);
|
||||
g_ptr_array_set_size(db->db_entries, size);
|
||||
for (unsigned int i = 0; i < size; i++) {
|
||||
if (__dbe_read(db, i))
|
||||
|
@ -137,7 +135,7 @@ struct db_entry *db_insert(struct database *db, const gchar *key)
|
|||
struct db_entry *item = NULL;
|
||||
|
||||
if (key)
|
||||
item = db->db_ops->dbe_alloc(key);
|
||||
item = db->db_ops->dbe_alloc(key, db_actual_size(db));
|
||||
|
||||
if (item) {
|
||||
g_ptr_array_add(db->db_entries, item);
|
||||
|
|
|
@ -26,14 +26,14 @@ void date_today(struct date *date)
|
|||
|
||||
void date_read(struct file *f, struct date *date)
|
||||
{
|
||||
file_readf(f, "%u %u %u", &date->d_year, &date->d_month, &date->d_day);
|
||||
date->d_year = file_readu(f);
|
||||
date->d_month = file_readu(f);
|
||||
date->d_day = file_readu(f);
|
||||
}
|
||||
|
||||
void date_read_stamp(struct file *f, struct date *date)
|
||||
{
|
||||
uint32_t stamp;
|
||||
file_readf(f, "%u", &stamp);
|
||||
date->d_stamp = be32toh(stamp);
|
||||
date->d_stamp = be32toh(file_readu(f));
|
||||
}
|
||||
|
||||
void date_write(struct file *f, struct date *date)
|
||||
|
|
257
core/file.c
257
core/file.c
|
@ -12,67 +12,43 @@
|
|||
g_printerr("%s (%s:%d): %s: %s\n", __func__, __FILE__, __LINE__, fname, error)
|
||||
#define REPORT_ERRNO(fname) REPORT_ERROR(fname, strerror(errno))
|
||||
|
||||
static gchar *__file_path(const gchar *base, const gchar *dir,
|
||||
const gchar *name)
|
||||
static void __file_init_common(struct file *file, const gchar *subdir,
|
||||
const gchar *name, unsigned int min,
|
||||
const gchar *(*user_dir)(void))
|
||||
{
|
||||
return g_build_filename(base, OCARINA_NAME, dir ? dir : "", name, NULL);
|
||||
file->f_file = NULL;
|
||||
file->f_name = name;
|
||||
file->f_subdir = subdir;
|
||||
file->f_mode = CLOSED;
|
||||
file->f_version = OCARINA_MINOR_VERSION;
|
||||
file->f_prev = 0;
|
||||
file->f_min = min;
|
||||
file->f_user_dir = user_dir;
|
||||
}
|
||||
|
||||
static gchar *__file_build_path(const gchar *base, const gchar *dir,
|
||||
const gchar *name)
|
||||
static bool __file_open(struct file *file, enum open_mode mode)
|
||||
{
|
||||
if (string_length(name) == 0)
|
||||
return g_strdup("");
|
||||
return __file_path(base, dir, name);
|
||||
}
|
||||
gchar *cmode, *path;
|
||||
|
||||
static gchar *__file_build_tmp(const gchar *base, const gchar *dir,
|
||||
const gchar *name)
|
||||
{
|
||||
gchar *tmp, *res;
|
||||
|
||||
if (string_length(name) == 0)
|
||||
return g_strdup("");
|
||||
|
||||
tmp = g_strdup_printf(".%s.tmp", name);
|
||||
res = __file_path(base, dir, tmp);
|
||||
g_free(tmp);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool __file_exists(gchar *path)
|
||||
{
|
||||
bool ret = g_file_test(path, G_FILE_TEST_EXISTS);
|
||||
g_free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static FILE *__file_open(gchar *path, const gchar *mode)
|
||||
{
|
||||
FILE *ret = g_fopen(path, mode);
|
||||
|
||||
if (!ret)
|
||||
REPORT_ERRNO(path);
|
||||
g_free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __file_close(FILE *file, gchar *path, gchar *tmp)
|
||||
{
|
||||
if (file) {
|
||||
fclose(file);
|
||||
if (path && tmp)
|
||||
g_rename(tmp, path);
|
||||
if (mode == OPEN_READ || mode == OPEN_READ_BINARY) {
|
||||
cmode = "r";
|
||||
path = file_path(file);
|
||||
} else {
|
||||
cmode = "w";
|
||||
path = file_write_path(file);
|
||||
}
|
||||
|
||||
file->f_file = g_fopen(path, cmode);
|
||||
if (!file->f_file)
|
||||
REPORT_ERRNO(path);
|
||||
g_free(path);
|
||||
g_free(tmp);
|
||||
return file->f_file != NULL;
|
||||
}
|
||||
|
||||
static bool __file_mkdir(const gchar *basedir, const gchar *subdir)
|
||||
static bool __file_mkdir(struct file *file)
|
||||
{
|
||||
gchar *dir = __file_path(basedir, subdir, NULL);
|
||||
gchar *dir = g_build_filename(file->f_user_dir(), OCARINA_NAME,
|
||||
file->f_subdir, NULL);
|
||||
int ret = g_mkdir_with_parents(dir, 0755);
|
||||
|
||||
if (ret != 0)
|
||||
|
@ -94,43 +70,38 @@ static bool __file_can_write(struct file *file)
|
|||
}
|
||||
|
||||
|
||||
void file_init(struct file *file, const gchar *name, unsigned int min)
|
||||
void file_init_data(struct file *file, const gchar *subdir,
|
||||
const gchar *name, unsigned int min)
|
||||
{
|
||||
file->f_mode = OPEN_READ;
|
||||
file->f_version = OCARINA_MINOR_VERSION;
|
||||
file->f_prev = 0;
|
||||
file->f_min = min;
|
||||
file->f_file = NULL;
|
||||
file->f_name = name;
|
||||
__file_init_common(file, subdir, name, min, g_get_user_data_dir);
|
||||
}
|
||||
|
||||
void cache_file_init(struct cache_file *file, gchar *subdir, gchar *name)
|
||||
void file_init_cache(struct file *file, const gchar *subdir, const gchar *name)
|
||||
{
|
||||
file->cf_file = NULL;
|
||||
file->cf_name = name;
|
||||
file->cf_subdir = subdir;
|
||||
__file_init_common(file, subdir, name, 0, g_get_user_cache_dir);
|
||||
}
|
||||
|
||||
gchar *file_path(struct file *file)
|
||||
{
|
||||
return __file_build_path(g_get_user_data_dir(), NULL, file->f_name);
|
||||
}
|
||||
|
||||
gchar *cache_file_path(struct cache_file *file)
|
||||
{
|
||||
return __file_build_path(g_get_user_cache_dir(), file->cf_subdir,
|
||||
file->cf_name);
|
||||
if (string_length(file->f_name) == 0)
|
||||
return g_strdup("");
|
||||
return g_build_filename(file->f_user_dir(), OCARINA_NAME,
|
||||
file->f_subdir, file->f_name, NULL);
|
||||
}
|
||||
|
||||
gchar *file_write_path(struct file *file)
|
||||
{
|
||||
return __file_build_tmp(g_get_user_data_dir(), NULL, file->f_name);
|
||||
}
|
||||
gchar *tmp, *res;
|
||||
|
||||
gchar *cache_file_write_path(struct cache_file *file)
|
||||
{
|
||||
return __file_build_tmp(g_get_user_cache_dir(), file->cf_subdir,
|
||||
file->cf_name);
|
||||
if (string_length(file->f_name) == 0)
|
||||
return g_strdup("");
|
||||
|
||||
tmp = g_strdup_printf(".%s.tmp", file->f_name);
|
||||
res = g_build_filename(file->f_user_dir(), OCARINA_NAME,
|
||||
file->f_subdir, tmp, NULL);
|
||||
g_free(tmp);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const unsigned int file_version(struct file *file)
|
||||
|
@ -142,26 +113,24 @@ const unsigned int file_version(struct file *file)
|
|||
|
||||
bool file_exists(struct file *file)
|
||||
{
|
||||
return __file_exists(file_path(file));
|
||||
gchar *path = file_path(file);
|
||||
bool ret = g_file_test(path, G_FILE_TEST_EXISTS);
|
||||
g_free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool cache_file_exists(struct cache_file *file)
|
||||
{
|
||||
return __file_exists(cache_file_path(file));
|
||||
}
|
||||
|
||||
static bool __file_open_read(struct file *file)
|
||||
static bool __file_open_read(struct file *file, enum open_mode mode)
|
||||
{
|
||||
if (!file_exists(file))
|
||||
return false;
|
||||
|
||||
file->f_file = __file_open(file_path(file), "r");
|
||||
if (!file->f_file)
|
||||
if (!__file_open(file, mode))
|
||||
return false;
|
||||
|
||||
file->f_mode = OPEN_READ;
|
||||
if (file_readf(file, "%u\n", &file->f_prev) != 1)
|
||||
return false;
|
||||
file->f_mode = mode;
|
||||
if (mode == OPEN_READ_BINARY)
|
||||
return true;
|
||||
|
||||
file->f_prev = file_readu(file);
|
||||
if (file->f_prev < file->f_min) {
|
||||
REPORT_ERROR(file->f_name, "File too old to be upgraded.");
|
||||
file_close(file);
|
||||
|
@ -175,18 +144,18 @@ static bool __file_open_read(struct file *file)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool __file_open_write(struct file *file)
|
||||
static bool __file_open_write(struct file *file, enum open_mode mode)
|
||||
{
|
||||
if (!__file_mkdir(g_get_user_data_dir(), NULL))
|
||||
if (!__file_mkdir(file))
|
||||
return false;
|
||||
if (!__file_can_write(file))
|
||||
return false;
|
||||
|
||||
file->f_file = __file_open(file_write_path(file), "w");
|
||||
if (!file->f_file)
|
||||
if (!__file_open(file, OPEN_WRITE))
|
||||
return false;
|
||||
|
||||
file->f_mode = OPEN_WRITE;
|
||||
file->f_mode = mode;
|
||||
if (mode == OPEN_WRITE_BINARY)
|
||||
return true;
|
||||
return file_writef(file, "%d\n", file->f_version) > 0;
|
||||
}
|
||||
|
||||
|
@ -194,62 +163,49 @@ bool file_open(struct file *file, enum open_mode mode)
|
|||
{
|
||||
if ((string_length(file->f_name) == 0) || (file->f_file != NULL))
|
||||
return false;
|
||||
|
||||
if (mode == OPEN_READ)
|
||||
return __file_open_read(file);
|
||||
return __file_open_write(file);
|
||||
}
|
||||
|
||||
bool cache_file_open(struct cache_file *file, enum open_mode mode)
|
||||
{
|
||||
if (mode == OPEN_READ)
|
||||
return false;
|
||||
if ((string_length(file->cf_name) == 0) || (file->cf_file != NULL))
|
||||
return false;
|
||||
if (!__file_mkdir(g_get_user_cache_dir(), file->cf_subdir))
|
||||
if (mode == CLOSED)
|
||||
return false;
|
||||
|
||||
file->cf_file = __file_open(cache_file_write_path(file), "wb");
|
||||
return file->cf_file != NULL;
|
||||
if (mode == OPEN_READ || mode == OPEN_READ_BINARY)
|
||||
return __file_open_read(file, mode);
|
||||
return __file_open_write(file, mode);
|
||||
}
|
||||
|
||||
void file_close(struct file *file)
|
||||
{
|
||||
__file_close(file->f_file,
|
||||
file->f_mode == OPEN_WRITE ? file_path(file) : NULL,
|
||||
file->f_mode == OPEN_WRITE ? file_write_path(file) : NULL);
|
||||
gchar *path = file_path(file);
|
||||
gchar *tmp = file_write_path(file);
|
||||
|
||||
if (file->f_file) {
|
||||
fclose(file->f_file);
|
||||
if (file->f_mode == OPEN_WRITE || file->f_mode == OPEN_WRITE_BINARY)
|
||||
g_rename(tmp, path);
|
||||
}
|
||||
|
||||
file->f_file = NULL;
|
||||
file->f_mode = CLOSED;
|
||||
|
||||
g_free(path);
|
||||
g_free(tmp);
|
||||
}
|
||||
|
||||
void cache_file_close(struct cache_file *file)
|
||||
gchar *file_readw(struct file *file)
|
||||
{
|
||||
__file_close(file->cf_file,
|
||||
cache_file_path(file),
|
||||
cache_file_write_path(file));
|
||||
file->cf_file = NULL;
|
||||
}
|
||||
|
||||
int file_readf(struct file *file, const char *fmt, ...)
|
||||
{
|
||||
va_list argp;
|
||||
int ret;
|
||||
|
||||
va_start(argp, fmt);
|
||||
ret = vfscanf(file->f_file, fmt, argp);
|
||||
va_end(argp);
|
||||
|
||||
return ret;
|
||||
gchar *s;
|
||||
return fscanf(file->f_file, "%ms%*c", &s) ? s : g_strdup("");
|
||||
}
|
||||
|
||||
gchar *file_readl(struct file *file)
|
||||
{
|
||||
gchar *res;
|
||||
gchar *s = NULL;
|
||||
size_t len = 0;
|
||||
return getline(&s, &len, file->f_file) ? g_strchomp(s) : g_strdup("");
|
||||
}
|
||||
|
||||
if (file_readf(file, "%m[^\n]\n", &res) == 0)
|
||||
return g_strdup("");
|
||||
|
||||
g_strstrip(res);
|
||||
return res;
|
||||
unsigned int file_readu(struct file *file)
|
||||
{
|
||||
unsigned int u;
|
||||
return fscanf(file->f_file, "%u%*c", &u) ? u : 0;
|
||||
}
|
||||
|
||||
int file_writef(struct file *file, const char *fmt, ...)
|
||||
|
@ -266,36 +222,57 @@ int file_writef(struct file *file, const char *fmt, ...)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int cache_file_write(struct cache_file *file, const void *data, size_t len)
|
||||
gchar *file_read(struct file *file)
|
||||
{
|
||||
if (fwrite(data, len, 1, file->cf_file) == 1)
|
||||
int fd = fileno(file->f_file);
|
||||
struct stat st;
|
||||
gchar *buf;
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return NULL;
|
||||
|
||||
buf = g_malloc0(st.st_size + 1);
|
||||
if (fread(buf, st.st_size, 1, file->f_file) == 1)
|
||||
return buf;
|
||||
|
||||
g_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int file_write(struct file *file, const void *data, size_t len)
|
||||
{
|
||||
if (fwrite(data, len, 1, file->f_file) == 1)
|
||||
return len;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool cache_file_import(struct cache_file *file, const gchar *srcpath)
|
||||
bool file_import(struct file *file, const gchar *srcpath)
|
||||
{
|
||||
gchar *contents = NULL;
|
||||
gsize length = 0;
|
||||
|
||||
if (!file->cf_file || !srcpath)
|
||||
if (!file->f_file || !srcpath)
|
||||
return false;
|
||||
if (!g_file_get_contents(srcpath, &contents, &length, NULL))
|
||||
return false;
|
||||
|
||||
cache_file_write(file, contents, length);
|
||||
file_write(file, contents, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool file_remove(struct file *file)
|
||||
{
|
||||
gchar *path, *dir;
|
||||
int ret = -1;
|
||||
gchar *path;
|
||||
|
||||
if (!file->f_file) {
|
||||
path = file_path(file);
|
||||
ret = g_unlink(path);
|
||||
dir = g_path_get_dirname(path);
|
||||
if (string_length(file->f_subdir) > 0)
|
||||
g_rmdir(dir);
|
||||
g_free(path);
|
||||
g_free(dir);
|
||||
}
|
||||
|
||||
return ret == 0;
|
||||
|
|
47
core/idle.c
47
core/idle.c
|
@ -8,11 +8,11 @@
|
|||
struct idle_task {
|
||||
bool (*idle_func)(void *);
|
||||
void *idle_data;
|
||||
enum idle_sync_t idle_sync;
|
||||
};
|
||||
|
||||
static GThreadPool *idle_pool = NULL;
|
||||
static GQueue idle_queue = G_QUEUE_INIT;
|
||||
static GThreadPool *idle_pool = NULL;
|
||||
static GQueue idle_queue = G_QUEUE_INIT;
|
||||
static enum idle_sync_t idle_mode = IDLE_SYNC;
|
||||
static unsigned int queued = 0;
|
||||
static unsigned int serviced = 0;
|
||||
|
||||
|
@ -38,17 +38,11 @@ void __idle_thread(gpointer task, gpointer data)
|
|||
}
|
||||
|
||||
|
||||
void idle_init()
|
||||
void idle_init(enum idle_sync_t sync)
|
||||
{
|
||||
idle_pool = g_thread_pool_new(__idle_thread, NULL, 1, true, NULL);
|
||||
idle_mode = sync;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TESTING
|
||||
void idle_init_sync()
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_TESTING */
|
||||
|
||||
void idle_deinit()
|
||||
{
|
||||
struct idle_task *task;
|
||||
|
@ -58,8 +52,10 @@ void idle_deinit()
|
|||
g_free(task);
|
||||
}
|
||||
|
||||
if (idle_pool)
|
||||
g_thread_pool_free(idle_pool, true, false);
|
||||
if (idle_pool) {
|
||||
g_thread_pool_free(idle_pool, true, true);
|
||||
idle_pool = NULL;
|
||||
}
|
||||
|
||||
queued = 0;
|
||||
serviced = 0;
|
||||
|
@ -67,12 +63,24 @@ void idle_deinit()
|
|||
|
||||
void idle_schedule(enum idle_sync_t sync, bool (*func)(void *), void *data)
|
||||
{
|
||||
struct idle_task *task = g_malloc(sizeof(struct idle_task));
|
||||
struct idle_task *task;
|
||||
|
||||
if (sync == IDLE_ASYNC && idle_mode == IDLE_SYNC)
|
||||
return;
|
||||
|
||||
task = g_malloc(sizeof(struct idle_task));
|
||||
task->idle_func = func;
|
||||
task->idle_data = data;
|
||||
task->idle_sync = sync;
|
||||
|
||||
g_queue_push_tail(&idle_queue, task);
|
||||
if (sync == IDLE_SYNC)
|
||||
g_queue_push_tail(&idle_queue, task);
|
||||
else {
|
||||
if (!idle_pool)
|
||||
idle_pool = g_thread_pool_new(__idle_thread, NULL, 1,
|
||||
false, NULL);
|
||||
g_thread_pool_push(idle_pool, task, NULL);
|
||||
}
|
||||
|
||||
g_atomic_int_inc(&queued);
|
||||
}
|
||||
|
||||
|
@ -82,12 +90,7 @@ bool idle_run_task()
|
|||
|
||||
if (!g_queue_is_empty(&idle_queue)) {
|
||||
task = g_queue_pop_head(&idle_queue);
|
||||
if (task->idle_sync == IDLE_ASYNC) {
|
||||
if (idle_pool)
|
||||
g_thread_pool_push(idle_pool, task, NULL);
|
||||
else
|
||||
__idle_free_task(task);
|
||||
} else if (!__idle_run_task(task))
|
||||
if (!__idle_run_task(task))
|
||||
g_queue_push_tail(&idle_queue, task);
|
||||
}
|
||||
|
||||
|
|
244
core/playlist.c
244
core/playlist.c
|
@ -10,6 +10,9 @@ static const gchar *SETTINGS_CUR_ID = "core.playlist.cur.id";
|
|||
static const gchar *SETTINGS_PREV_TYPE = "core.playlist.prev.type";
|
||||
static const gchar *SETTINGS_PREV_ID = "core.playlist.prev.id";
|
||||
|
||||
static struct playlist *current = NULL;
|
||||
static struct playlist *previous = NULL;
|
||||
|
||||
struct playlist_type *playlist_types[] = {
|
||||
[PL_SYSTEM] = &pl_system,
|
||||
[PL_ARTIST] = &pl_artist,
|
||||
|
@ -18,19 +21,30 @@ struct playlist_type *playlist_types[] = {
|
|||
};
|
||||
|
||||
|
||||
void playlist_init(struct queue_ops *ops)
|
||||
static struct playlist *__playlist_saved(const gchar *s_type, const gchar *s_id)
|
||||
{
|
||||
pl_system_init(ops);
|
||||
pl_artist_init(ops);
|
||||
pl_user_init(ops);
|
||||
pl_library_init(ops);
|
||||
unsigned int type, id;
|
||||
|
||||
if (!settings_has(SETTINGS_CUR_TYPE) ||
|
||||
!settings_has(SETTINGS_CUR_ID)) {
|
||||
playlist_select(PL_SYSTEM, "Collection");
|
||||
if (playlist_size(PL_SYSTEM, "Queued Tracks") > 0)
|
||||
playlist_select(PL_SYSTEM, "Queued Tracks");
|
||||
}
|
||||
if (!settings_has(s_type) || !settings_has(s_id))
|
||||
return NULL;
|
||||
|
||||
type = settings_get(s_type);
|
||||
id = settings_get(s_id);
|
||||
return playlist_types[type]->pl_get(id);
|
||||
}
|
||||
|
||||
void playlist_init(struct playlist_callbacks *cb)
|
||||
{
|
||||
playlist_generic_set_callbacks(cb);
|
||||
pl_system_init();
|
||||
pl_artist_init();
|
||||
pl_user_init();
|
||||
pl_library_init();
|
||||
|
||||
current = __playlist_saved(SETTINGS_CUR_TYPE, SETTINGS_CUR_ID);
|
||||
previous = __playlist_saved(SETTINGS_PREV_TYPE, SETTINGS_PREV_ID);
|
||||
if (!current)
|
||||
current = playlist_lookup(PL_SYSTEM, "Collection");
|
||||
}
|
||||
|
||||
void playlist_deinit()
|
||||
|
@ -48,123 +62,175 @@ void playlist_save()
|
|||
playlist_types[i]->pl_save();
|
||||
}
|
||||
|
||||
bool playlist_select(enum playlist_type_t type, const gchar *name)
|
||||
void playlist_played(struct track *track)
|
||||
{
|
||||
if (!playlist_types[type]->pl_can_select(name))
|
||||
return false;
|
||||
if ((settings_get(SETTINGS_CUR_TYPE) == type) &&
|
||||
settings_get(SETTINGS_CUR_ID) == playlist_get_id(type, name))
|
||||
return true;
|
||||
unsigned int i;
|
||||
|
||||
settings_set(SETTINGS_PREV_TYPE, settings_get(SETTINGS_CUR_TYPE));
|
||||
settings_set(SETTINGS_PREV_ID, settings_get(SETTINGS_CUR_ID));
|
||||
settings_set(SETTINGS_CUR_TYPE, type);
|
||||
settings_set(SETTINGS_CUR_ID, playlist_get_id(type, name));
|
||||
return true;
|
||||
if (track && !TRACK_IS_EXTERNAL(track)) {
|
||||
for (i = 0; i < PL_MAX_TYPE; i++)
|
||||
playlist_types[i]->pl_played(track);
|
||||
}
|
||||
}
|
||||
|
||||
bool playlist_new(enum playlist_type_t type, const gchar *name)
|
||||
void playlist_selected(struct track *track)
|
||||
{
|
||||
return playlist_types[type]->pl_new(name);
|
||||
unsigned int i;
|
||||
if (track && !TRACK_IS_EXTERNAL(track)) {
|
||||
for (i = 0; i < PL_MAX_TYPE; i++)
|
||||
playlist_types[i]->pl_selected(track);
|
||||
|
||||
if (playlist_size(current) == 0)
|
||||
playlist_select(previous);
|
||||
}
|
||||
}
|
||||
|
||||
bool playlist_delete(enum playlist_type_t type, const gchar *name)
|
||||
struct playlist *playlist_new(enum playlist_type_t type, const gchar *name)
|
||||
{
|
||||
return playlist_types[type]->pl_delete(name);
|
||||
if (type < PL_MAX_TYPE && playlist_types[type]->pl_new)
|
||||
return playlist_types[type]->pl_new(name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool playlist_add(enum playlist_type_t type, const gchar *name,
|
||||
struct track *track)
|
||||
bool playlist_delete(struct playlist *playlist)
|
||||
{
|
||||
bool ret;
|
||||
enum playlist_type_t type;
|
||||
bool ret;
|
||||
|
||||
if (!track)
|
||||
if (!playlist || !playlist->pl_ops->pl_delete)
|
||||
return false;
|
||||
|
||||
ret = playlist_types[type]->pl_add_track(name, track);
|
||||
if (type == PL_SYSTEM && string_match(name, "Queued Tracks"))
|
||||
playlist_select(PL_SYSTEM, "Queued Tracks");
|
||||
type = playlist->pl_type;
|
||||
ret = playlist->pl_ops->pl_delete(playlist);
|
||||
if (ret)
|
||||
playlist_types[type]->pl_save();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool playlist_remove(enum playlist_type_t type, const gchar *name,
|
||||
struct track *track)
|
||||
struct playlist *playlist_lookup(enum playlist_type_t type, const gchar *name)
|
||||
{
|
||||
if (!track)
|
||||
if (type >= PL_MAX_TYPE)
|
||||
return NULL;
|
||||
return playlist_types[type]->pl_lookup(name);
|
||||
}
|
||||
|
||||
struct playlist *playlist_get(enum playlist_type_t type, unsigned int id)
|
||||
{
|
||||
if (type >= PL_MAX_TYPE)
|
||||
return NULL;
|
||||
return playlist_types[type]->pl_get(id);
|
||||
}
|
||||
|
||||
struct playlist *playlist_current(void)
|
||||
{
|
||||
return current;
|
||||
}
|
||||
|
||||
bool playlist_select(struct playlist *playlist)
|
||||
{
|
||||
if (!playlist || (playlist == current))
|
||||
return false;
|
||||
return playlist_types[type]->pl_remove_track(name, track);
|
||||
}
|
||||
|
||||
void playlist_update(enum playlist_type_t type, const gchar *name)
|
||||
{
|
||||
playlist_types[type]->pl_update(name);
|
||||
}
|
||||
|
||||
bool playlist_has(enum playlist_type_t type, const gchar *name,
|
||||
struct track *track)
|
||||
{
|
||||
struct queue *queue = playlist_get_queue(type, name);
|
||||
if (!track || !queue)
|
||||
if (!playlist->pl_ops->pl_can_select)
|
||||
return false;
|
||||
if (!playlist->pl_ops->pl_can_select(playlist))
|
||||
return false;
|
||||
return queue_has(queue, track);
|
||||
}
|
||||
|
||||
unsigned int playlist_size(enum playlist_type_t type, const gchar *name)
|
||||
{
|
||||
struct queue *queue = playlist_get_queue(type, name);
|
||||
return queue ? queue_size(queue) : 0;
|
||||
}
|
||||
previous = current;
|
||||
current = playlist;
|
||||
|
||||
void playlist_set_random(enum playlist_type_t type, const gchar *name,
|
||||
bool enabled)
|
||||
{
|
||||
playlist_types[type]->pl_set_flag(name, Q_RANDOM, enabled);
|
||||
}
|
||||
settings_set(SETTINGS_CUR_TYPE, current->pl_type);
|
||||
settings_set(SETTINGS_CUR_ID, current->pl_id);
|
||||
|
||||
bool playlist_get_random(enum playlist_type_t type, const gchar *name)
|
||||
{
|
||||
struct queue *queue = playlist_get_queue(type, name);
|
||||
return queue ? queue_has_flag(queue, Q_RANDOM) : false;
|
||||
}
|
||||
|
||||
void playlist_sort(enum playlist_type_t type, const gchar *name,
|
||||
enum compare_t sort, bool reset)
|
||||
{
|
||||
playlist_types[type]->pl_sort(name, sort, reset);
|
||||
if (previous) {
|
||||
settings_set(SETTINGS_PREV_TYPE, previous->pl_type);
|
||||
settings_set(SETTINGS_PREV_ID, previous->pl_id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct track *playlist_next(void)
|
||||
{
|
||||
enum playlist_type_t type = settings_get(SETTINGS_CUR_TYPE);
|
||||
unsigned int id = settings_get(SETTINGS_CUR_ID);
|
||||
gchar *name = playlist_get_name(type, id);
|
||||
struct track *track = playlist_types[type]->pl_next(name);
|
||||
|
||||
if (playlist_size(type, name) == 0) {
|
||||
settings_set(SETTINGS_CUR_ID, settings_get(SETTINGS_PREV_ID));
|
||||
settings_set(SETTINGS_CUR_TYPE, settings_get(SETTINGS_PREV_TYPE));
|
||||
}
|
||||
|
||||
g_free(name);
|
||||
struct track *track = playlist_generic_next(current);
|
||||
if (track && current->pl_type < PL_MAX_TYPE)
|
||||
playlist_types[current->pl_type]->pl_save();
|
||||
return track;
|
||||
}
|
||||
|
||||
struct track *playlist_prev(void)
|
||||
{
|
||||
return playlist_types[PL_SYSTEM]->pl_next("History");
|
||||
return playlist_generic_next(playlist_lookup(PL_SYSTEM, "History"));
|
||||
}
|
||||
|
||||
struct queue *playlist_get_queue(enum playlist_type_t type, const gchar *name)
|
||||
bool playlist_add(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
return playlist_types[type]->pl_get_queue(name);
|
||||
bool ret;
|
||||
|
||||
if (!track || !playlist || !playlist->pl_ops->pl_add)
|
||||
return false;
|
||||
|
||||
ret = playlist->pl_ops->pl_add(playlist, track);
|
||||
if (ret && playlist->pl_type < PL_MAX_TYPE)
|
||||
playlist_types[playlist->pl_type]->pl_save();
|
||||
if (playlist == playlist_lookup(PL_SYSTEM, "Queued Tracks"))
|
||||
playlist_select(playlist);
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int playlist_get_id(enum playlist_type_t type, const gchar *name)
|
||||
bool playlist_remove(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
return playlist_types[type]->pl_get_id(name);
|
||||
bool ret;
|
||||
|
||||
if (!track || !playlist || !playlist->pl_ops->pl_remove)
|
||||
return false;
|
||||
|
||||
ret = playlist->pl_ops->pl_remove(playlist, track);
|
||||
if (ret && playlist->pl_type < PL_MAX_TYPE)
|
||||
playlist_types[playlist->pl_type]->pl_save();
|
||||
return ret;
|
||||
}
|
||||
|
||||
gchar *playlist_get_name(enum playlist_type_t type, unsigned int id)
|
||||
void playlist_set_random(struct playlist *playlist, bool enabled)
|
||||
{
|
||||
return playlist_types[type]->pl_get_name(id);
|
||||
if (playlist && playlist->pl_ops->pl_set_random) {
|
||||
playlist->pl_ops->pl_set_random(playlist, enabled);
|
||||
if (playlist->pl_type < PL_MAX_TYPE)
|
||||
playlist_types[playlist->pl_type]->pl_save();
|
||||
}
|
||||
}
|
||||
|
||||
bool playlist_sort(struct playlist *playlist, enum compare_t sort)
|
||||
{
|
||||
if (!playlist || !playlist->pl_ops->pl_sort)
|
||||
return false;
|
||||
|
||||
playlist->pl_ops->pl_sort(playlist, sort);
|
||||
if (playlist->pl_type < PL_MAX_TYPE)
|
||||
playlist_types[playlist->pl_type]->pl_save();
|
||||
return g_slist_length(playlist->pl_sort) > 0;
|
||||
}
|
||||
|
||||
bool playlist_rearrange(struct playlist *playlist, unsigned int old_pos,
|
||||
unsigned int new_pos)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
if (!playlist || !playlist->pl_ops->pl_rearrange)
|
||||
return false;
|
||||
|
||||
ret = playlist->pl_ops->pl_rearrange(playlist, old_pos, new_pos);
|
||||
if (ret && playlist->pl_type < PL_MAX_TYPE)
|
||||
playlist_types[playlist->pl_type]->pl_save();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void playlist_set_search(struct playlist *playlist, const gchar *text)
|
||||
{
|
||||
gchar **tokens = NULL;
|
||||
|
||||
if (!playlist)
|
||||
return;
|
||||
if (playlist->pl_search)
|
||||
g_strfreev(playlist->pl_search);
|
||||
if (strlen(text) > 0)
|
||||
tokens = g_str_tokenize_and_fold(text, NULL, NULL);
|
||||
playlist->pl_search = tokens;
|
||||
}
|
||||
|
|
|
@ -5,27 +5,21 @@
|
|||
#include <core/playlists/artist.h>
|
||||
#include <core/string.h>
|
||||
|
||||
static struct queue_ops *artist_ops = NULL;
|
||||
static struct file artist_file = FILE_INIT("playlist.artist", 0);
|
||||
static struct file artist_file = FILE_INIT_DATA("", "playlist.artist", 0);
|
||||
|
||||
static struct playlist_ops pl_artist_ops = {
|
||||
.pl_can_select = playlist_generic_can_select,
|
||||
.pl_set_random = playlist_generic_set_random,
|
||||
.pl_sort = playlist_generic_sort,
|
||||
.pl_rearrange = playlist_generic_rearrange,
|
||||
};
|
||||
|
||||
|
||||
static struct playlist *__artist_pl_alloc(gchar *name)
|
||||
static struct playlist *__artist_pl_alloc(struct artist *artist)
|
||||
{
|
||||
struct playlist *playlist = g_malloc(sizeof(struct playlist));
|
||||
|
||||
playlist->pl_name = name;
|
||||
playlist->pl_type = PL_ARTIST;
|
||||
playlist_generic_init(playlist, Q_REPEAT, artist_ops);
|
||||
|
||||
return playlist;
|
||||
}
|
||||
|
||||
static void __artist_pl_free(struct playlist *playlist)
|
||||
{
|
||||
if (playlist) {
|
||||
queue_deinit(&playlist->pl_queue);
|
||||
g_free(playlist);
|
||||
}
|
||||
return playlist_generic_alloc(artist->ar_name, PL_ARTIST,
|
||||
artist_index(artist), &pl_artist_ops,
|
||||
3, COMPARE_YEAR, COMPARE_ALBUM, COMPARE_TRACK);
|
||||
}
|
||||
|
||||
static bool __artist_pl_add(void *data)
|
||||
|
@ -34,13 +28,12 @@ static bool __artist_pl_add(void *data)
|
|||
struct artist *artist = artist_lookup(playlist->pl_name);
|
||||
struct db_entry *dbe, *next;
|
||||
|
||||
queue_set_flag(&playlist->pl_queue, Q_ADD_FRONT);
|
||||
db_for_each(dbe, next, track_db_get()) {
|
||||
if (TRACK(dbe)->tr_album->al_artist == artist)
|
||||
queue_add(&playlist->pl_queue, TRACK(dbe));
|
||||
playlist_generic_add_front(playlist, TRACK(dbe));
|
||||
}
|
||||
queue_unset_flag(&playlist->pl_queue, Q_ADD_FRONT);
|
||||
|
||||
playlist_generic_resort(playlist);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -59,17 +52,14 @@ static bool __artist_pl_load(void *data)
|
|||
if (!file_open(&artist_file, OPEN_READ))
|
||||
return true;
|
||||
|
||||
file_readf(&artist_file, "%u\n", &n);
|
||||
n = file_readu(&artist_file);
|
||||
for (i = 0; i < n; i++) {
|
||||
name = file_readl(&artist_file);
|
||||
playlist = __artist_pl_lookup(name);
|
||||
if (playlist)
|
||||
playlist_generic_load(playlist, &artist_file,
|
||||
PL_SAVE_METADATA);
|
||||
g_free(name);
|
||||
|
||||
if (!playlist)
|
||||
continue;
|
||||
queue_load_flags(&playlist->pl_queue, &artist_file, true);
|
||||
queue_iter_set(&playlist->pl_queue, &playlist->pl_queue.q_cur,
|
||||
playlist->pl_queue.q_cur.it_pos);
|
||||
}
|
||||
|
||||
file_close(&artist_file);
|
||||
|
@ -89,100 +79,46 @@ static void pl_artist_save(void)
|
|||
db_for_each(dbe, next, artist_db_get()) {
|
||||
playlist = ARTIST(dbe)->ar_playlist;
|
||||
file_writef(&artist_file, "%s\n", playlist->pl_name);
|
||||
queue_save_flags(&playlist->pl_queue, &artist_file, true);
|
||||
playlist_generic_save(playlist, &artist_file, PL_SAVE_METADATA);
|
||||
}
|
||||
|
||||
file_close(&artist_file);
|
||||
}
|
||||
|
||||
static struct queue *pl_artist_get_queue(const gchar *name)
|
||||
static struct playlist *pl_artist_lookup(const gchar *name)
|
||||
{
|
||||
struct playlist *playlist = __artist_pl_lookup(name);
|
||||
return playlist ? &playlist->pl_queue : NULL;
|
||||
return __artist_pl_lookup(name);
|
||||
}
|
||||
|
||||
static unsigned int pl_artist_get_id(const gchar *name)
|
||||
static struct playlist *pl_artist_get(unsigned int id)
|
||||
{
|
||||
struct artist *artist = artist_lookup(name);
|
||||
return artist ? artist->ar_dbe.dbe_index : -1;
|
||||
struct artist *artist = artist_get(id);
|
||||
return artist ? artist->ar_playlist : NULL;
|
||||
}
|
||||
|
||||
static bool pl_artist_can_select(const gchar *name)
|
||||
static void pl_artist_played(struct track *track)
|
||||
{
|
||||
struct playlist *playlist = __artist_pl_lookup(name);
|
||||
return playlist ? playlist_generic_can_select(playlist) : false;
|
||||
}
|
||||
|
||||
static gchar *pl_artist_get_name(unsigned int id)
|
||||
{
|
||||
struct artist *artist = ARTIST(db_at(artist_db_get(), id));
|
||||
return artist ? g_strdup(artist->ar_name) : NULL;
|
||||
}
|
||||
|
||||
static bool pl_artist_new_delete(const gchar *name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool pl_artist_add_rm(const gchar *name, struct track *track)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static void pl_artist_update(const gchar *name)
|
||||
{
|
||||
}
|
||||
|
||||
static void pl_artist_set_flag(const gchar *name, enum queue_flags flag,
|
||||
bool enabled)
|
||||
{
|
||||
struct playlist *playlist = __artist_pl_lookup(name);
|
||||
playlist_generic_set_flag(playlist, flag, enabled);
|
||||
pl_artist_save();
|
||||
}
|
||||
|
||||
static void pl_artist_sort(const gchar *name, enum compare_t sort, bool reset)
|
||||
{
|
||||
struct playlist *playlist = __artist_pl_lookup(name);
|
||||
playlist_generic_sort(playlist, sort, reset);
|
||||
pl_artist_save();
|
||||
}
|
||||
|
||||
static struct track *pl_artist_next(const gchar *name)
|
||||
{
|
||||
struct playlist *playlist = __artist_pl_lookup(name);
|
||||
struct track *track = playlist_generic_next(playlist);;
|
||||
pl_artist_save();
|
||||
return track;
|
||||
struct artist *artist = track->tr_album->al_artist;
|
||||
playlist_generic_update(artist->ar_playlist, track);
|
||||
}
|
||||
|
||||
|
||||
struct playlist_type pl_artist = {
|
||||
.pl_save = pl_artist_save,
|
||||
.pl_get_queue = pl_artist_get_queue,
|
||||
.pl_get_id = pl_artist_get_id,
|
||||
.pl_get_name = pl_artist_get_name,
|
||||
.pl_can_select = pl_artist_can_select,
|
||||
.pl_new = pl_artist_new_delete,
|
||||
.pl_delete = pl_artist_new_delete,
|
||||
.pl_add_track = pl_artist_add_rm,
|
||||
.pl_remove_track = pl_artist_add_rm,
|
||||
.pl_update = pl_artist_update,
|
||||
.pl_set_flag = pl_artist_set_flag,
|
||||
.pl_sort = pl_artist_sort,
|
||||
.pl_next = pl_artist_next,
|
||||
.pl_save = pl_artist_save,
|
||||
.pl_lookup = pl_artist_lookup,
|
||||
.pl_get = pl_artist_get,
|
||||
.pl_played = pl_artist_played,
|
||||
.pl_selected = pl_artist_played,
|
||||
};
|
||||
|
||||
|
||||
void pl_artist_init(struct queue_ops *ops)
|
||||
void pl_artist_init(void)
|
||||
{
|
||||
struct db_entry *dbe, *next;
|
||||
struct playlist *playlist;
|
||||
|
||||
artist_ops = ops;
|
||||
|
||||
db_for_each(dbe, next, artist_db_get()) {
|
||||
playlist = __artist_pl_alloc(ARTIST(dbe)->ar_name);
|
||||
playlist = __artist_pl_alloc(ARTIST(dbe));
|
||||
ARTIST(dbe)->ar_playlist = playlist;
|
||||
|
||||
idle_schedule(IDLE_SYNC, __artist_pl_add, playlist);
|
||||
|
@ -194,13 +130,10 @@ void pl_artist_init(struct queue_ops *ops)
|
|||
void pl_artist_deinit()
|
||||
{
|
||||
struct db_entry *dbe, *next;
|
||||
struct playlist *playlist;
|
||||
|
||||
db_for_each(dbe, next, artist_db_get()) {
|
||||
playlist = ARTIST(dbe)->ar_playlist;
|
||||
playlist_generic_free(ARTIST(dbe)->ar_playlist);
|
||||
ARTIST(dbe)->ar_playlist = NULL;
|
||||
|
||||
__artist_pl_free(playlist);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,18 +143,15 @@ void pl_artist_new_track(struct track *track)
|
|||
struct playlist *playlist = (struct playlist *)artist->ar_playlist;
|
||||
|
||||
if (!playlist) {
|
||||
playlist = __artist_pl_alloc(artist->ar_name);
|
||||
playlist = __artist_pl_alloc(artist);
|
||||
artist->ar_playlist = playlist;
|
||||
}
|
||||
|
||||
playlist_generic_add_track(playlist, track);
|
||||
playlist_generic_add(playlist, track);
|
||||
}
|
||||
|
||||
void pl_artist_delete_track(struct track *track)
|
||||
{
|
||||
struct artist *artist = track->tr_album->al_artist;
|
||||
struct playlist *playlist = (struct playlist *)artist->ar_playlist;
|
||||
|
||||
if (playlist)
|
||||
playlist_generic_remove_track(playlist, track);
|
||||
struct artist *artist = track->tr_album->al_artist;
|
||||
playlist_generic_remove(artist->ar_playlist, track);
|
||||
}
|
||||
|
|
|
@ -2,117 +2,312 @@
|
|||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/idle.h>
|
||||
#include <core/playlists/type.h>
|
||||
#include <core/playlists/generic.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static struct playlist_callbacks *callbacks = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* Noop playlist operations.
|
||||
*/
|
||||
bool playlist_noop_can_select(struct playlist *playlist)
|
||||
static int __playlist_generic_find_sort(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
return false;
|
||||
return abs(GPOINTER_TO_INT(a)) - abs(GPOINTER_TO_INT(b));
|
||||
}
|
||||
|
||||
void playlist_noop_clear(struct playlist *playlist)
|
||||
static int __playlist_generic_less_than(gconstpointer a, gconstpointer b,
|
||||
gpointer data)
|
||||
{
|
||||
}
|
||||
struct track *lhs = (struct track *)a;
|
||||
struct track *rhs = (struct track *)b;
|
||||
GSList *cur = ((struct playlist *)data)->pl_sort;
|
||||
int res, field;
|
||||
|
||||
void playlist_noop_set_flag(struct playlist *playlist,
|
||||
enum queue_flags flag, bool enabled)
|
||||
{
|
||||
}
|
||||
while (cur) {
|
||||
field = GPOINTER_TO_INT(cur->data);
|
||||
res = track_compare(lhs, rhs, abs(field));
|
||||
if (res != 0)
|
||||
break;
|
||||
cur = g_slist_next(cur);
|
||||
};
|
||||
|
||||
void playlist_noop_sort(struct playlist *playlist,
|
||||
enum compare_t sort, bool reset)
|
||||
{
|
||||
return (field > 0) ? res : -res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generic playlist operations.
|
||||
*/
|
||||
void playlist_generic_init(struct playlist *playlist, unsigned int flags,
|
||||
struct queue_ops *ops)
|
||||
void playlist_generic_set_callbacks(struct playlist_callbacks *cb)
|
||||
{
|
||||
queue_init(&playlist->pl_queue, flags | Q_ENABLED, ops, playlist);
|
||||
queue_sort(&playlist->pl_queue, COMPARE_ARTIST, true);
|
||||
queue_sort(&playlist->pl_queue, COMPARE_YEAR, false);
|
||||
queue_sort(&playlist->pl_queue, COMPARE_TRACK, false);
|
||||
callbacks = cb;
|
||||
}
|
||||
|
||||
static void __playlist_generic_vinit(struct playlist *playlist,
|
||||
unsigned int nargs, va_list argp)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (!playlist)
|
||||
return;
|
||||
|
||||
g_queue_init(&playlist->pl_tracks);
|
||||
playlist->pl_length = 0;
|
||||
playlist->pl_random = false;
|
||||
playlist->pl_current = NULL;
|
||||
playlist->pl_sort = NULL;
|
||||
playlist->pl_search = NULL;
|
||||
|
||||
for (i = 0; i < nargs; i++)
|
||||
playlist_generic_sort(playlist, va_arg(argp, unsigned int));
|
||||
}
|
||||
|
||||
void playlist_generic_init(struct playlist *playlist, unsigned int nargs, ...)
|
||||
{
|
||||
va_list argp;
|
||||
|
||||
va_start(argp, nargs);
|
||||
__playlist_generic_vinit(playlist, nargs, argp);
|
||||
va_end(argp);
|
||||
}
|
||||
|
||||
void playlist_generic_deinit(struct playlist *playlist)
|
||||
{
|
||||
if (playlist) {
|
||||
playlist_generic_clear(playlist);
|
||||
playlist_clear_sort(playlist);
|
||||
g_strfreev(playlist->pl_search);
|
||||
playlist->pl_search = NULL;
|
||||
playlist->pl_length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct playlist *playlist_generic_alloc(gchar *name, enum playlist_type_t type,
|
||||
unsigned int id, struct playlist_ops *ops,
|
||||
unsigned int nargs, ...)
|
||||
{
|
||||
struct playlist *playlist = g_malloc(sizeof(struct playlist));
|
||||
va_list argp;
|
||||
|
||||
playlist->pl_name = name;
|
||||
playlist->pl_type = type;
|
||||
playlist->pl_id = id;
|
||||
playlist->pl_ops = ops;
|
||||
|
||||
va_start(argp, nargs);
|
||||
__playlist_generic_vinit(playlist, nargs, argp);
|
||||
if (callbacks)
|
||||
callbacks->pl_cb_alloc(playlist);
|
||||
va_end(argp);
|
||||
|
||||
return playlist;
|
||||
}
|
||||
|
||||
void playlist_generic_free(struct playlist *playlist)
|
||||
{
|
||||
if (playlist) {
|
||||
playlist_generic_deinit(playlist);
|
||||
g_free(playlist);
|
||||
}
|
||||
}
|
||||
|
||||
void playlist_generic_save(struct playlist *playlist, struct file *file,
|
||||
unsigned int flags)
|
||||
{
|
||||
playlist_iter it;
|
||||
GSList *sort;
|
||||
int field;
|
||||
|
||||
if (!playlist)
|
||||
return;
|
||||
|
||||
if (flags & PL_SAVE_ITER)
|
||||
file_writef(file, "%u ", playlist_current_index(playlist));
|
||||
|
||||
if (flags & PL_SAVE_FLAGS) {
|
||||
sort = playlist->pl_sort;
|
||||
file_writef(file, "%u ", playlist->pl_random ? PL_RANDOM : 0);
|
||||
file_writef(file, "%u", g_slist_length(sort));
|
||||
while (sort) {
|
||||
field = GPOINTER_TO_INT(sort->data);
|
||||
file_writef(file, " %u %d", abs(field) - 1, field > 0);
|
||||
sort = g_slist_next(sort);
|
||||
}
|
||||
file_writef(file, "\n");
|
||||
}
|
||||
|
||||
if (flags & PL_SAVE_TRACKS) {
|
||||
file_writef(file, "%u", playlist_size(playlist));
|
||||
playlist_for_each(playlist, it)
|
||||
file_writef(file, " %u",
|
||||
track_index(playlist_iter_track(it)));
|
||||
file_writef(file, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
void playlist_generic_load(struct playlist *playlist, struct file *file,
|
||||
unsigned int flags)
|
||||
{
|
||||
unsigned int f, n, i, t, it = 0;
|
||||
int field, ascending;
|
||||
|
||||
if (!playlist)
|
||||
return;
|
||||
|
||||
if (flags & PL_SAVE_ITER)
|
||||
it = file_readu(file);
|
||||
|
||||
if (flags & PL_SAVE_FLAGS) {
|
||||
f = file_readu(file);
|
||||
n = file_readu(file);
|
||||
playlist_clear_sort(playlist);
|
||||
for (i = 0; i < n; i++) {
|
||||
field = file_readu(file) + 1;
|
||||
ascending = file_readd(file);
|
||||
if (!ascending)
|
||||
field = -field;
|
||||
playlist->pl_sort = g_slist_append(playlist->pl_sort,
|
||||
GINT_TO_POINTER(field));
|
||||
}
|
||||
playlist_generic_resort(playlist);
|
||||
}
|
||||
|
||||
if (flags & PL_SAVE_TRACKS) {
|
||||
n = file_readu(file);
|
||||
for (i = 0; i < n; i++) {
|
||||
t = file_readu(file);
|
||||
playlist_generic_add(playlist, track_get(t));
|
||||
}
|
||||
}
|
||||
|
||||
playlist_generic_set_random(playlist, f == PL_RANDOM);
|
||||
playlist_current_set(playlist, it);
|
||||
}
|
||||
|
||||
bool playlist_generic_can_select(struct playlist *playlist)
|
||||
{
|
||||
return queue_size(&playlist->pl_queue) > 0;
|
||||
return playlist_size(playlist) > 0;
|
||||
}
|
||||
|
||||
void playlist_generic_clear(struct playlist *playlist)
|
||||
{
|
||||
queue_clear(&playlist->pl_queue);
|
||||
}
|
||||
unsigned int n;
|
||||
|
||||
bool playlist_generic_add_track(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
if (queue_has(&playlist->pl_queue, track))
|
||||
return false;
|
||||
queue_add(&playlist->pl_queue, track);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool playlist_generic_remove_track(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
return queue_remove_all(&playlist->pl_queue, track);
|
||||
}
|
||||
|
||||
struct pl_update_data {
|
||||
struct playlist *pud_playlist;
|
||||
bool (*pud_func)(struct playlist *, struct track *);
|
||||
};
|
||||
|
||||
static bool __playlist_generic_update(void *data)
|
||||
{
|
||||
struct pl_update_data *pud = data;
|
||||
struct db_entry *dbe, *next;
|
||||
|
||||
db_for_each(dbe, next, track_db_get()) {
|
||||
if (!pud->pud_func(pud->pud_playlist, TRACK(dbe)))
|
||||
playlist_generic_remove_track(pud->pud_playlist, TRACK(dbe));
|
||||
}
|
||||
|
||||
queue_unset_flag(&pud->pud_playlist->pl_queue, Q_ADD_FRONT);
|
||||
g_free(pud);
|
||||
return true;
|
||||
}
|
||||
|
||||
void playlist_generic_update(struct playlist *playlist,
|
||||
bool (*func)(struct playlist *, struct track *))
|
||||
{
|
||||
struct pl_update_data *data;
|
||||
|
||||
if (!func)
|
||||
if (!playlist)
|
||||
return;
|
||||
|
||||
data = g_malloc(sizeof(struct pl_update_data));
|
||||
data->pud_playlist = playlist;
|
||||
data->pud_func = func;
|
||||
|
||||
idle_schedule(IDLE_SYNC, __playlist_generic_update, data);
|
||||
n = playlist_size(playlist);
|
||||
g_queue_clear(&playlist->pl_tracks);
|
||||
playlist->pl_length = 0;
|
||||
playlist->pl_current = NULL;
|
||||
if (callbacks)
|
||||
callbacks->pl_cb_removed(playlist, NULL, n);
|
||||
}
|
||||
|
||||
void playlist_generic_set_flag(struct playlist *playlist,
|
||||
enum queue_flags flag, bool enabled)
|
||||
bool playlist_generic_add(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
if (enabled)
|
||||
return queue_set_flag(&playlist->pl_queue, flag);
|
||||
return queue_unset_flag(&playlist->pl_queue, flag);
|
||||
if (!playlist || !track || playlist_has(playlist, track))
|
||||
return false;
|
||||
|
||||
playlist->pl_length += track->tr_length;
|
||||
if (playlist->pl_sort) {
|
||||
g_queue_insert_sorted(&playlist->pl_tracks, track,
|
||||
__playlist_generic_less_than, playlist);
|
||||
} else
|
||||
g_queue_push_tail(&playlist->pl_tracks, track);
|
||||
|
||||
if (callbacks)
|
||||
callbacks->pl_cb_added(playlist, track);
|
||||
return true;
|
||||
}
|
||||
|
||||
void playlist_generic_sort(struct playlist *playlist,
|
||||
enum compare_t sort, bool reset)
|
||||
bool playlist_generic_add_front(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
queue_sort(&playlist->pl_queue, sort, reset);
|
||||
if (!playlist || !track)
|
||||
return false;
|
||||
|
||||
playlist->pl_length += track->tr_length;
|
||||
g_queue_push_head(&playlist->pl_tracks, track);
|
||||
if (callbacks)
|
||||
callbacks->pl_cb_added(playlist, track);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool playlist_generic_remove(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
unsigned int count;
|
||||
|
||||
if (!playlist || !track)
|
||||
return false;
|
||||
|
||||
while (playlist_current_track(playlist) == track)
|
||||
playlist_current_previous(playlist);
|
||||
|
||||
count = g_queue_remove_all(&playlist->pl_tracks, track);
|
||||
playlist->pl_length -= (count * track->tr_length);
|
||||
|
||||
if (callbacks)
|
||||
callbacks->pl_cb_removed(playlist, track, count);
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
void playlist_generic_update(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
if (playlist && callbacks)
|
||||
callbacks->pl_cb_updated(playlist, track);
|
||||
}
|
||||
|
||||
void playlist_generic_set_random(struct playlist *playlist, bool enabled)
|
||||
{
|
||||
playlist->pl_random = enabled;
|
||||
}
|
||||
|
||||
void playlist_generic_sort(struct playlist *playlist, enum compare_t field)
|
||||
{
|
||||
gpointer sort = GINT_TO_POINTER(field);
|
||||
GSList *found = g_slist_find_custom(playlist->pl_sort, sort,
|
||||
__playlist_generic_find_sort);
|
||||
|
||||
if (found)
|
||||
found->data = GINT_TO_POINTER(-field);
|
||||
else
|
||||
playlist->pl_sort = g_slist_append(playlist->pl_sort, sort);
|
||||
|
||||
playlist_generic_resort(playlist);
|
||||
}
|
||||
|
||||
void playlist_generic_resort(struct playlist *playlist)
|
||||
{
|
||||
if (!playlist || !playlist->pl_sort)
|
||||
return;
|
||||
|
||||
g_queue_sort(&playlist->pl_tracks, __playlist_generic_less_than, playlist);
|
||||
playlist_generic_update(playlist, NULL);
|
||||
}
|
||||
|
||||
bool playlist_generic_rearrange(struct playlist *playlist, unsigned int old_pos,
|
||||
unsigned int new_pos)
|
||||
{
|
||||
GList *nth;
|
||||
|
||||
if (old_pos == new_pos || old_pos >= playlist_size(playlist) ||
|
||||
new_pos > playlist_size(playlist))
|
||||
return false;
|
||||
|
||||
playlist_clear_sort(playlist);
|
||||
nth = g_queue_pop_nth_link(&playlist->pl_tracks, old_pos);
|
||||
g_queue_push_nth_link(&playlist->pl_tracks, new_pos, nth);
|
||||
playlist_generic_update(playlist, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct track *playlist_generic_next(struct playlist *playlist)
|
||||
{
|
||||
return queue_next(&playlist->pl_queue);
|
||||
unsigned int pos, size = playlist_size(playlist);
|
||||
|
||||
if (size == 0)
|
||||
return NULL;
|
||||
else if (playlist->pl_random) {
|
||||
pos = playlist_current_index(playlist);
|
||||
pos += g_random_int_range(1, size);
|
||||
playlist_current_set(playlist, pos % size);
|
||||
} else if (!playlist_current_next(playlist))
|
||||
playlist_current_set(playlist, 0);
|
||||
|
||||
return playlist_current_track(playlist);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <core/playlists/artist.h>
|
||||
#include <core/playlists/library.h>
|
||||
#include <core/playlists/system.h>
|
||||
#include <core/playlists/user.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct scan_data {
|
||||
|
@ -13,27 +14,17 @@ struct scan_data {
|
|||
};
|
||||
|
||||
static bool __lib_pl_scan_dir(void *);
|
||||
static struct queue_ops *lib_ops = NULL;
|
||||
static struct file lib_file = FILE_INIT("playlist.library", 0);
|
||||
static struct file lib_file = FILE_INIT_DATA("", "playlist.library", 0);
|
||||
|
||||
static struct playlist_ops pl_library_ops;
|
||||
|
||||
|
||||
static struct playlist *__lib_pl_alloc(struct library *library)
|
||||
{
|
||||
struct playlist *playlist = g_malloc(sizeof(struct playlist));
|
||||
|
||||
playlist->pl_name = library->li_path;
|
||||
playlist->pl_type = PL_LIBRARY;
|
||||
playlist_generic_init(playlist, Q_REPEAT, lib_ops);
|
||||
|
||||
return playlist;
|
||||
}
|
||||
|
||||
static void __lib_pl_free(struct playlist *playlist)
|
||||
{
|
||||
if (playlist) {
|
||||
queue_deinit(&playlist->pl_queue);
|
||||
g_free(playlist);
|
||||
}
|
||||
return playlist_generic_alloc(library->li_path, PL_LIBRARY,
|
||||
library_index(library), &pl_library_ops,
|
||||
4, COMPARE_ARTIST, COMPARE_YEAR,
|
||||
COMPARE_ALBUM, COMPARE_TRACK);
|
||||
}
|
||||
|
||||
static bool __lib_pl_add(void *data)
|
||||
|
@ -42,13 +33,12 @@ static bool __lib_pl_add(void *data)
|
|||
struct library *library = library_lookup(playlist->pl_name);
|
||||
struct db_entry *dbe, *next;
|
||||
|
||||
queue_set_flag(&playlist->pl_queue, Q_ADD_FRONT);
|
||||
db_for_each(dbe, next, track_db_get()) {
|
||||
if (TRACK(dbe)->tr_library == library)
|
||||
queue_add(&playlist->pl_queue, TRACK(dbe));
|
||||
playlist_generic_add_front(playlist, TRACK(dbe));
|
||||
}
|
||||
queue_unset_flag(&playlist->pl_queue, Q_ADD_FRONT);
|
||||
|
||||
playlist_generic_resort(playlist);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -67,17 +57,14 @@ static bool __lib_pl_load(void *data)
|
|||
if (!file_open(&lib_file, OPEN_READ))
|
||||
return true;
|
||||
|
||||
file_readf(&lib_file, "%u\n", &n);
|
||||
n = file_readu(&lib_file);
|
||||
for (i = 0; i < n; i++) {
|
||||
name = file_readl(&lib_file);
|
||||
playlist = __lib_pl_lookup(name);
|
||||
if (playlist)
|
||||
playlist_generic_load(playlist, &lib_file,
|
||||
PL_SAVE_METADATA);
|
||||
g_free(name);
|
||||
|
||||
if (!playlist)
|
||||
continue;
|
||||
queue_load_flags(&playlist->pl_queue, &lib_file, true);
|
||||
queue_iter_set(&playlist->pl_queue, &playlist->pl_queue.q_cur,
|
||||
playlist->pl_queue.q_cur.it_pos);
|
||||
}
|
||||
|
||||
file_close(&lib_file);
|
||||
|
@ -106,7 +93,7 @@ static void __lib_pl_read_path(struct scan_data *scan, const gchar *name)
|
|||
else {
|
||||
track = track_add(scan->sd_library, path);
|
||||
if (track) {
|
||||
queue_add(&playlist->pl_queue, track);
|
||||
playlist_generic_add(playlist, track);
|
||||
pl_system_new_track(track);
|
||||
pl_artist_new_track(track);
|
||||
}
|
||||
|
@ -156,7 +143,7 @@ static bool __lib_pl_update(void *data)
|
|||
if (g_access(path, F_OK) < 0) {
|
||||
pl_system_delete_track(TRACK(dbe));
|
||||
pl_artist_delete_track(TRACK(dbe));
|
||||
queue_remove_all(&playlist->pl_queue, TRACK(dbe));
|
||||
playlist_generic_remove(playlist, TRACK(dbe));
|
||||
track_remove(TRACK(dbe));
|
||||
}
|
||||
g_free(path);
|
||||
|
@ -168,6 +155,36 @@ static bool __lib_pl_update(void *data)
|
|||
}
|
||||
|
||||
|
||||
static bool pl_library_delete(struct playlist *playlist)
|
||||
{
|
||||
struct library *library = library_lookup(playlist->pl_name);
|
||||
playlist_iter it;
|
||||
|
||||
if (!library)
|
||||
return false;
|
||||
|
||||
playlist_for_each(playlist, it) {
|
||||
pl_system_delete_track(playlist_iter_track(it));
|
||||
pl_artist_delete_track(playlist_iter_track(it));
|
||||
pl_user_delete_track(playlist_iter_track(it));
|
||||
}
|
||||
playlist_generic_free(playlist);
|
||||
|
||||
track_remove_all(library);
|
||||
library_remove(library);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static struct playlist_ops pl_library_ops = {
|
||||
.pl_can_select = playlist_generic_can_select,
|
||||
.pl_delete = pl_library_delete,
|
||||
.pl_set_random = playlist_generic_set_random,
|
||||
.pl_sort = playlist_generic_sort,
|
||||
.pl_rearrange = playlist_generic_rearrange,
|
||||
};
|
||||
|
||||
|
||||
static void pl_library_save(void)
|
||||
{
|
||||
struct db_entry *dbe, *next;
|
||||
|
@ -180,137 +197,65 @@ static void pl_library_save(void)
|
|||
db_for_each(dbe, next, library_db_get()) {
|
||||
playlist = LIBRARY(dbe)->li_playlist;
|
||||
file_writef(&lib_file, "%s\n", playlist->pl_name);
|
||||
queue_save_flags(&playlist->pl_queue, &lib_file, true);
|
||||
playlist_generic_save(playlist, &lib_file, PL_SAVE_METADATA);
|
||||
}
|
||||
|
||||
file_close(&lib_file);
|
||||
}
|
||||
|
||||
static struct queue *pl_library_get_queue(const gchar *name)
|
||||
static struct playlist *pl_library_lookup(const gchar *name)
|
||||
{
|
||||
struct playlist *playlist = __lib_pl_lookup(name);
|
||||
return playlist ? &playlist->pl_queue : NULL;
|
||||
return __lib_pl_lookup(name);
|
||||
}
|
||||
|
||||
static unsigned int pl_library_get_id(const gchar *name)
|
||||
{
|
||||
struct library *library = library_find(name);
|
||||
return library ? library->li_dbe.dbe_index : -1;
|
||||
}
|
||||
|
||||
static gchar *pl_library_get_name(unsigned int id)
|
||||
static struct playlist *pl_library_get(unsigned int id)
|
||||
{
|
||||
struct library *library = LIBRARY(db_at(library_db_get(), id));
|
||||
return library ? g_strdup(library->li_path) : NULL;
|
||||
return library ? library->li_playlist : NULL;
|
||||
}
|
||||
|
||||
static bool pl_library_can_select(const gchar *name)
|
||||
{
|
||||
struct playlist *playlist = __lib_pl_lookup(name);
|
||||
return playlist ? playlist_generic_can_select(playlist) : false;
|
||||
}
|
||||
|
||||
static bool pl_library_new(const gchar *name)
|
||||
static struct playlist *pl_library_new(const gchar *name)
|
||||
{
|
||||
struct library *library;
|
||||
|
||||
if (__lib_pl_lookup(name) || !g_file_test(name, G_FILE_TEST_IS_DIR))
|
||||
return false;
|
||||
return NULL;
|
||||
|
||||
library = library_find(name);
|
||||
library->li_playlist = __lib_pl_alloc(library);
|
||||
|
||||
__lib_pl_scan_dir_idle(library, name);
|
||||
return true;
|
||||
return library->li_playlist;
|
||||
}
|
||||
|
||||
static bool pl_library_delete(const gchar *name)
|
||||
static void pl_library_played(struct track *track)
|
||||
{
|
||||
struct playlist *playlist = __lib_pl_lookup(name);
|
||||
struct library *library = library_lookup(name);
|
||||
struct queue_iter it;
|
||||
|
||||
if (!library)
|
||||
return false;
|
||||
|
||||
queue_for_each(&playlist->pl_queue, &it) {
|
||||
pl_system_delete_track(queue_iter_val(&it));
|
||||
pl_artist_delete_track(queue_iter_val(&it));
|
||||
}
|
||||
__lib_pl_free(playlist);
|
||||
|
||||
track_remove_all(library);
|
||||
library_remove(library);
|
||||
pl_library_save();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pl_library_add_rm(const gchar *name, struct track *track)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static void pl_library_update(const gchar *name)
|
||||
{
|
||||
struct playlist *playlist = __lib_pl_lookup(name);
|
||||
if (playlist)
|
||||
idle_schedule(IDLE_SYNC, __lib_pl_update, playlist);
|
||||
}
|
||||
|
||||
static void pl_library_set_flag(const gchar *name, enum queue_flags flag,
|
||||
bool enabled)
|
||||
{
|
||||
struct playlist *playlist = __lib_pl_lookup(name);
|
||||
playlist_generic_set_flag(playlist, flag, enabled);
|
||||
pl_library_save();
|
||||
}
|
||||
|
||||
static void pl_library_sort(const gchar *name, enum compare_t sort, bool reset)
|
||||
{
|
||||
struct playlist *playlist = __lib_pl_lookup(name);
|
||||
playlist_generic_sort(playlist, sort, reset);
|
||||
pl_library_save();
|
||||
}
|
||||
|
||||
static struct track *pl_library_next(const gchar *name)
|
||||
{
|
||||
struct playlist *playlist = __lib_pl_lookup(name);
|
||||
struct track *track = playlist_generic_next(playlist);
|
||||
pl_library_save();
|
||||
return track;
|
||||
struct library *library = track->tr_library;
|
||||
playlist_generic_update(library->li_playlist, track);
|
||||
}
|
||||
|
||||
|
||||
struct playlist_type pl_library = {
|
||||
.pl_save = pl_library_save,
|
||||
.pl_get_queue = pl_library_get_queue,
|
||||
.pl_get_id = pl_library_get_id,
|
||||
.pl_get_name = pl_library_get_name,
|
||||
.pl_can_select = pl_library_can_select,
|
||||
.pl_new = pl_library_new,
|
||||
.pl_delete = pl_library_delete,
|
||||
.pl_add_track = pl_library_add_rm,
|
||||
.pl_remove_track = pl_library_add_rm,
|
||||
.pl_update = pl_library_update,
|
||||
.pl_set_flag = pl_library_set_flag,
|
||||
.pl_sort = pl_library_sort,
|
||||
.pl_next = pl_library_next,
|
||||
.pl_save = pl_library_save,
|
||||
.pl_lookup = pl_library_lookup,
|
||||
.pl_get = pl_library_get,
|
||||
.pl_new = pl_library_new,
|
||||
.pl_played = pl_library_played,
|
||||
.pl_selected = pl_library_played,
|
||||
};
|
||||
|
||||
|
||||
void pl_library_init(struct queue_ops *ops)
|
||||
void pl_library_init(void)
|
||||
{
|
||||
struct db_entry *dbe, *next;
|
||||
struct playlist *playlist;
|
||||
|
||||
lib_ops = ops;
|
||||
|
||||
db_for_each(dbe, next, library_db_get()) {
|
||||
playlist = __lib_pl_alloc(LIBRARY(dbe));
|
||||
LIBRARY(dbe)->li_playlist = playlist;
|
||||
|
||||
idle_schedule(IDLE_SYNC, __lib_pl_add, playlist);
|
||||
pl_library_update(playlist->pl_name);
|
||||
idle_schedule(IDLE_SYNC, __lib_pl_add, playlist);
|
||||
idle_schedule(IDLE_SYNC, __lib_pl_update, playlist);
|
||||
}
|
||||
|
||||
idle_schedule(IDLE_SYNC, __lib_pl_load, NULL);
|
||||
|
@ -319,12 +264,14 @@ void pl_library_init(struct queue_ops *ops)
|
|||
void pl_library_deinit()
|
||||
{
|
||||
struct db_entry *dbe, *next;
|
||||
struct playlist *playlist;
|
||||
|
||||
db_for_each(dbe, next, library_db_get()) {
|
||||
playlist = LIBRARY(dbe)->li_playlist;
|
||||
playlist_generic_free(LIBRARY(dbe)->li_playlist);
|
||||
LIBRARY(dbe)->li_playlist = NULL;
|
||||
|
||||
__lib_pl_free(playlist);
|
||||
}
|
||||
}
|
||||
|
||||
void pl_library_update(struct playlist *playlist)
|
||||
{
|
||||
idle_schedule(IDLE_SYNC, __lib_pl_update, playlist);
|
||||
}
|
||||
|
|
|
@ -5,83 +5,84 @@
|
|||
#include <core/playlists/system.h>
|
||||
#include <core/string.h>
|
||||
|
||||
static bool pl_system_add_track(const gchar *, struct track *);
|
||||
static bool pl_system_remove_track(const gchar *, struct track *);
|
||||
static void pl_system_update(const gchar *);
|
||||
static inline struct queue *__sys_pl_queue(enum sys_playlist_t);
|
||||
static void __sys_pl_save();
|
||||
static bool __sys_pl_load();
|
||||
static struct playlist *pl_system_lookup(const gchar *);
|
||||
static struct playlist *pl_system_get(unsigned int);
|
||||
static void pl_system_save();
|
||||
|
||||
static struct file sys_file = FILE_INIT("playlist.db", 0);
|
||||
static struct file sys_deck_f = FILE_INIT("deck", 1);
|
||||
static struct file sys_collection_f = FILE_INIT("library.q", 0);
|
||||
static struct file sys_pl_file = FILE_INIT("playlist.system", 0);
|
||||
static struct sys_playlist *sys_playlists[SYS_PL_NUM_PLAYLISTS];
|
||||
static struct file sys_file = FILE_INIT_DATA("", "playlist.db", 0);
|
||||
static struct file sys_deck_f = FILE_INIT_DATA("", "deck", 1);
|
||||
static struct file sys_collection_f = FILE_INIT_DATA("", "library.q", 0);
|
||||
static struct file sys_pl_file = FILE_INIT_DATA("", "playlist.system", 0);
|
||||
|
||||
|
||||
/*
|
||||
* Generic system playlist operations.
|
||||
*/
|
||||
static void sys_pl_generic_init(struct playlist *playlist, unsigned int flags,
|
||||
struct queue_ops *ops)
|
||||
static bool sys_pl_delete_clear(struct playlist *playlist)
|
||||
{
|
||||
playlist_generic_init(playlist, Q_REPEAT | Q_ADD_FRONT, ops);
|
||||
playlist_generic_clear(playlist);
|
||||
pl_system_save();
|
||||
return false;
|
||||
}
|
||||
|
||||
static void sys_pl_update_init(struct playlist *playlist, unsigned int flags,
|
||||
struct queue_ops *ops)
|
||||
static bool sys_pl_update_check(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
sys_pl_generic_init(playlist, flags, ops);
|
||||
pl_system_update(playlist->pl_name);
|
||||
switch (playlist->pl_id) {
|
||||
case SYS_PL_UNPLAYED:
|
||||
return track->tr_count == 0;
|
||||
case SYS_PL_LEAST_PLAYED:
|
||||
return track->tr_count <= track_db_average_plays() &&
|
||||
track->tr_count > 0;
|
||||
case SYS_PL_MOST_PLAYED:
|
||||
return track->tr_count > track_db_average_plays();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sys_pl_save_partial(struct playlist *playlist, struct file *file)
|
||||
static bool sys_pl_update_func(void *data)
|
||||
{
|
||||
file_writef(file, "%s\n", playlist->pl_name);
|
||||
queue_save_flags(&playlist->pl_queue, file, true);
|
||||
struct playlist *playlist = (struct playlist *)data;
|
||||
struct db_entry *dbe, *next;
|
||||
|
||||
db_for_each(dbe, next, track_db_get()) {
|
||||
struct track *track = TRACK(dbe);
|
||||
|
||||
if (!sys_pl_update_check(playlist, track))
|
||||
playlist_generic_remove(playlist, track);
|
||||
else if (!playlist_has(pl_system_get(SYS_PL_HIDDEN), track) &&
|
||||
!playlist_has(playlist, track))
|
||||
playlist_generic_add_front(playlist, track);
|
||||
}
|
||||
|
||||
playlist_generic_resort(playlist);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sys_pl_save_full(struct playlist *playlist, struct file *file)
|
||||
static void sys_pl_update(struct playlist *playlist)
|
||||
{
|
||||
sys_pl_save_partial(playlist, file);
|
||||
queue_save_tracks(&playlist->pl_queue, file);
|
||||
switch (playlist->pl_id) {
|
||||
case SYS_PL_COLLECTION:
|
||||
case SYS_PL_UNPLAYED:
|
||||
case SYS_PL_LEAST_PLAYED:
|
||||
case SYS_PL_MOST_PLAYED:
|
||||
idle_schedule(IDLE_SYNC, sys_pl_update_func, playlist);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void sys_pl_load_partial(struct playlist *playlist, struct file *file)
|
||||
{
|
||||
queue_load_flags(&playlist->pl_queue, file, true);
|
||||
}
|
||||
|
||||
static void sys_pl_load_full(struct playlist *playlist, struct file *file)
|
||||
{
|
||||
sys_pl_load_partial(playlist, file);
|
||||
queue_load_tracks(&playlist->pl_queue, file);
|
||||
}
|
||||
|
||||
static bool sys_pl_generic_add(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
if (queue_has(__sys_pl_queue(SYS_PL_HIDDEN), track))
|
||||
return false;
|
||||
return playlist_generic_add_track(playlist, track);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Favorite tracks playlist operations.
|
||||
*/
|
||||
static struct sys_playlist sys_favorites = {
|
||||
.spl_playlist = DEFINE_PLAYLIST(PL_SYSTEM, "Favorites"),
|
||||
.spl_init = playlist_generic_init,
|
||||
.spl_save = sys_pl_save_full,
|
||||
.spl_load = sys_pl_load_full,
|
||||
.spl_can_select = playlist_generic_can_select,
|
||||
.spl_add = playlist_generic_add_track,
|
||||
.spl_remove = playlist_generic_remove_track,
|
||||
.spl_clear = playlist_generic_clear,
|
||||
.spl_set_flag = playlist_generic_set_flag,
|
||||
.spl_sort = playlist_generic_sort,
|
||||
.spl_next = playlist_generic_next,
|
||||
static struct playlist_ops favorites_ops = {
|
||||
.pl_add = playlist_generic_add,
|
||||
.pl_can_select = playlist_generic_can_select,
|
||||
.pl_delete = sys_pl_delete_clear,
|
||||
.pl_remove = playlist_generic_remove,
|
||||
.pl_set_random = playlist_generic_set_random,
|
||||
.pl_sort = playlist_generic_sort,
|
||||
.pl_rearrange = playlist_generic_rearrange,
|
||||
};
|
||||
|
||||
|
||||
|
@ -90,46 +91,49 @@ static struct sys_playlist sys_favorites = {
|
|||
*/
|
||||
static bool sys_pl_hidden_add(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
bool ret = playlist_generic_add_track(playlist, track);
|
||||
pl_system_remove_track("Collection", track);
|
||||
pl_system_remove_track("Unplayed", track);
|
||||
pl_system_remove_track("Most Played", track);
|
||||
pl_system_remove_track("Least Played", track);
|
||||
bool ret = playlist_generic_add(pl_system_get(SYS_PL_HIDDEN), track);
|
||||
playlist_generic_remove(pl_system_get(SYS_PL_COLLECTION), track);
|
||||
playlist_generic_remove(pl_system_get(SYS_PL_UNPLAYED), track);
|
||||
playlist_generic_remove(pl_system_get(SYS_PL_MOST_PLAYED), track);
|
||||
playlist_generic_remove(pl_system_get(SYS_PL_LEAST_PLAYED), track);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool sys_pl_hidden_remove(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
bool ret = playlist_generic_remove_track(playlist, track);
|
||||
pl_system_add_track("Collection", track);
|
||||
pl_system_add_track("Unplayed", track);
|
||||
pl_system_add_track("Most Played", track);
|
||||
pl_system_add_track("Least Played", track);
|
||||
bool ret = playlist_generic_remove(playlist, track);
|
||||
unsigned int average = track_db_average_plays();
|
||||
unsigned int add_id = SYS_PL_LEAST_PLAYED;
|
||||
|
||||
add_id = (track->tr_count == 0) ? SYS_PL_UNPLAYED : add_id;
|
||||
add_id = (track->tr_count > average) ? SYS_PL_MOST_PLAYED : add_id;
|
||||
|
||||
playlist_generic_add(pl_system_get(SYS_PL_COLLECTION), track);
|
||||
playlist_generic_add(pl_system_get(add_id), track);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sys_pl_hidden_clear(struct playlist *playlist)
|
||||
static bool sys_pl_hidden_clear(struct playlist *playlist)
|
||||
{
|
||||
struct track *track;
|
||||
|
||||
while (queue_size(&playlist->pl_queue) > 0) {
|
||||
track = queue_at(&playlist->pl_queue, 0);
|
||||
while (playlist_size(playlist) > 0) {
|
||||
track = playlist_at(playlist, 0);
|
||||
sys_pl_hidden_remove(playlist, track);
|
||||
}
|
||||
|
||||
pl_system_save();
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct sys_playlist sys_hidden = {
|
||||
.spl_playlist = DEFINE_PLAYLIST(PL_SYSTEM, "Hidden"),
|
||||
.spl_init = playlist_generic_init,
|
||||
.spl_save = sys_pl_save_full,
|
||||
.spl_load = sys_pl_load_full,
|
||||
.spl_can_select = playlist_generic_can_select,
|
||||
.spl_add = sys_pl_hidden_add,
|
||||
.spl_remove = sys_pl_hidden_remove,
|
||||
.spl_clear = sys_pl_hidden_clear,
|
||||
.spl_set_flag = playlist_generic_set_flag,
|
||||
.spl_sort = playlist_generic_sort,
|
||||
.spl_next = playlist_generic_next,
|
||||
static struct playlist_ops hidden_ops = {
|
||||
.pl_add = sys_pl_hidden_add,
|
||||
.pl_can_select = playlist_generic_can_select,
|
||||
.pl_delete = sys_pl_hidden_clear,
|
||||
.pl_remove = sys_pl_hidden_remove,
|
||||
.pl_set_random = playlist_generic_set_random,
|
||||
.pl_sort = playlist_generic_sort,
|
||||
.pl_rearrange = playlist_generic_rearrange,
|
||||
};
|
||||
|
||||
|
||||
|
@ -138,20 +142,20 @@ static struct sys_playlist sys_hidden = {
|
|||
*/
|
||||
static bool sys_pl_queued_load()
|
||||
{
|
||||
struct playlist *playlist = &sys_playlists[SYS_PL_QUEUED]->spl_playlist;
|
||||
struct queue *queue = &playlist->pl_queue;
|
||||
struct playlist *playlist = pl_system_get(SYS_PL_QUEUED);
|
||||
unsigned int num, i, flags = 0;
|
||||
|
||||
if (!file_open(&sys_deck_f, OPEN_READ))
|
||||
return true;
|
||||
|
||||
file_readf(&sys_deck_f, "%u", &num);
|
||||
num = file_readu(&sys_deck_f);
|
||||
for (i = 0; i < num; i++) {
|
||||
file_readf(&sys_deck_f, "%u", &flags);
|
||||
flags &= ~(Q_SAVE_SORT | Q_SAVE_FLAGS);
|
||||
flags = file_readu(&sys_deck_f);
|
||||
flags &= PL_RANDOM;
|
||||
if (i == 0)
|
||||
queue->q_flags |= flags;
|
||||
queue_load_tracks(queue, &sys_deck_f);
|
||||
playlist_generic_set_random(playlist,
|
||||
flags == PL_RANDOM);
|
||||
playlist_generic_load(playlist, &sys_deck_f, PL_SAVE_TRACKS);
|
||||
}
|
||||
|
||||
file_close(&sys_deck_f);
|
||||
|
@ -159,24 +163,29 @@ static bool sys_pl_queued_load()
|
|||
return true;
|
||||
}
|
||||
|
||||
static void sys_pl_queued_init(struct playlist *playlist,
|
||||
unsigned int flags, struct queue_ops *ops)
|
||||
static bool sys_pl_queued_delete(struct playlist *playlist)
|
||||
{
|
||||
queue_init(&playlist->pl_queue, Q_ENABLED, ops, playlist);
|
||||
playlist_generic_set_random(playlist, false);
|
||||
playlist_clear_sort(playlist);
|
||||
return sys_pl_delete_clear(playlist);
|
||||
}
|
||||
|
||||
static struct sys_playlist sys_queued = {
|
||||
.spl_playlist = DEFINE_PLAYLIST(PL_SYSTEM, "Queued Tracks"),
|
||||
.spl_init = sys_pl_queued_init,
|
||||
.spl_save = sys_pl_save_full,
|
||||
.spl_load = sys_pl_load_full,
|
||||
.spl_can_select = playlist_generic_can_select,
|
||||
.spl_add = playlist_generic_add_track,
|
||||
.spl_remove = playlist_generic_remove_track,
|
||||
.spl_clear = playlist_generic_clear,
|
||||
.spl_set_flag = playlist_generic_set_flag,
|
||||
.spl_sort = playlist_generic_sort,
|
||||
.spl_next = playlist_generic_next,
|
||||
static bool sys_pl_queued_remove(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
bool ret = playlist_generic_remove(playlist, track);
|
||||
if (playlist_size(playlist) == 0)
|
||||
sys_pl_queued_delete(playlist);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct playlist_ops queued_ops = {
|
||||
.pl_add = playlist_generic_add,
|
||||
.pl_can_select = playlist_generic_can_select,
|
||||
.pl_delete = sys_pl_queued_delete,
|
||||
.pl_remove = sys_pl_queued_remove,
|
||||
.pl_set_random = playlist_generic_set_random,
|
||||
.pl_sort = playlist_generic_sort,
|
||||
.pl_rearrange = playlist_generic_rearrange,
|
||||
};
|
||||
|
||||
|
||||
|
@ -185,12 +194,10 @@ static struct sys_playlist sys_queued = {
|
|||
*/
|
||||
static bool sys_pl_collection_load()
|
||||
{
|
||||
struct playlist *playlist = &sys_playlists[SYS_PL_COLLECTION]->spl_playlist;
|
||||
struct playlist *playlist = pl_system_get(SYS_PL_COLLECTION);
|
||||
|
||||
if (file_open(&sys_collection_f, OPEN_READ)) {
|
||||
queue_load_flags(&playlist->pl_queue, &sys_collection_f, false);
|
||||
queue_unset_flag(&playlist->pl_queue, Q_SAVE_FLAGS);
|
||||
queue_unset_flag(&playlist->pl_queue, Q_SAVE_SORT);
|
||||
playlist_generic_load(playlist, &sys_collection_f, PL_SAVE_FLAGS);
|
||||
file_close(&sys_collection_f);
|
||||
file_remove(&sys_collection_f);
|
||||
}
|
||||
|
@ -198,227 +205,94 @@ static bool sys_pl_collection_load()
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool sys_pl_collection_can_select(struct playlist *playlist)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sys_pl_collection_update(struct playlist *playlist,
|
||||
struct track *track)
|
||||
{
|
||||
return sys_pl_generic_add(playlist, track) || true;
|
||||
}
|
||||
|
||||
static struct sys_playlist sys_collection = {
|
||||
.spl_playlist = DEFINE_PLAYLIST(PL_SYSTEM, "Collection"),
|
||||
.spl_init = sys_pl_update_init,
|
||||
.spl_save = sys_pl_save_partial,
|
||||
.spl_load = sys_pl_load_partial,
|
||||
.spl_can_select = sys_pl_collection_can_select,
|
||||
.spl_add = sys_pl_generic_add,
|
||||
.spl_remove = playlist_generic_remove_track,
|
||||
.spl_clear = playlist_noop_clear,
|
||||
.spl_update = sys_pl_collection_update,
|
||||
.spl_set_flag = playlist_generic_set_flag,
|
||||
.spl_sort = playlist_generic_sort,
|
||||
.spl_next = playlist_generic_next,
|
||||
static struct playlist_ops collection_ops = {
|
||||
.pl_can_select = playlist_generic_can_select,
|
||||
.pl_remove = sys_pl_hidden_add,
|
||||
.pl_set_random = playlist_generic_set_random,
|
||||
.pl_sort = playlist_generic_sort,
|
||||
.pl_rearrange = playlist_generic_rearrange,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* History playlist operations.
|
||||
*/
|
||||
static void sys_pl_history_init(struct playlist *playlist,
|
||||
unsigned int flags, struct queue_ops *ops)
|
||||
{
|
||||
queue_init(&playlist->pl_queue,
|
||||
Q_ENABLED | Q_REPEAT | Q_ADD_FRONT | Q_NO_SORT,
|
||||
ops, playlist);
|
||||
}
|
||||
|
||||
static bool sys_pl_history_add(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
queue_add(&playlist->pl_queue, track);
|
||||
queue_iter_set(&playlist->pl_queue, &playlist->pl_queue.q_cur, 0);
|
||||
playlist_generic_add_front(playlist, track);
|
||||
playlist_current_set(playlist, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct sys_playlist sys_history = {
|
||||
.spl_playlist = DEFINE_PLAYLIST(PL_SYSTEM, "History"),
|
||||
.spl_init = sys_pl_history_init,
|
||||
.spl_save = sys_pl_save_partial,
|
||||
.spl_load = sys_pl_load_partial,
|
||||
.spl_can_select = playlist_noop_can_select,
|
||||
.spl_add = sys_pl_history_add,
|
||||
.spl_remove = playlist_generic_remove_track,
|
||||
.spl_clear = playlist_noop_clear,
|
||||
.spl_set_flag = playlist_noop_set_flag,
|
||||
.spl_sort = playlist_noop_sort,
|
||||
.spl_next = playlist_generic_next,
|
||||
static struct playlist_ops history_ops = {
|
||||
.pl_add = sys_pl_history_add,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Unplayed tracks playlist operations.
|
||||
* Unplayed, Most Played, and Least Played tracks playlist operations.
|
||||
*/
|
||||
static bool sys_pl_unplayed_add(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
if (track->tr_count > 0)
|
||||
return false;
|
||||
return sys_pl_generic_add(playlist, track);
|
||||
}
|
||||
|
||||
static bool sys_pl_unplayed_update(struct playlist *playlist,
|
||||
struct track *track)
|
||||
{
|
||||
if (track->tr_count > 0)
|
||||
return false;
|
||||
return sys_pl_generic_add(playlist, track) || true;
|
||||
}
|
||||
|
||||
static struct sys_playlist sys_unplayed = {
|
||||
.spl_playlist = DEFINE_PLAYLIST(PL_SYSTEM, "Unplayed"),
|
||||
.spl_init = sys_pl_update_init,
|
||||
.spl_save = sys_pl_save_partial,
|
||||
.spl_load = sys_pl_load_partial,
|
||||
.spl_can_select = playlist_generic_can_select,
|
||||
.spl_add = sys_pl_unplayed_add,
|
||||
.spl_remove = playlist_generic_remove_track,
|
||||
.spl_clear = playlist_noop_clear,
|
||||
.spl_update = sys_pl_unplayed_update,
|
||||
.spl_set_flag = playlist_generic_set_flag,
|
||||
.spl_sort = playlist_generic_sort,
|
||||
.spl_next = playlist_generic_next,
|
||||
static struct playlist_ops dynamic_ops = {
|
||||
.pl_can_select = playlist_generic_can_select,
|
||||
.pl_set_random = playlist_generic_set_random,
|
||||
.pl_sort = playlist_generic_sort,
|
||||
.pl_rearrange = playlist_generic_rearrange,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Most played tracks playlist operations.
|
||||
*/
|
||||
static bool sys_pl_most_played_add(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
unsigned int average = track_db_average_plays();
|
||||
if (track->tr_count <= average)
|
||||
return false;
|
||||
return sys_pl_generic_add(playlist, track);
|
||||
}
|
||||
#define SYS_PLAYLIST(id, name, ops) \
|
||||
[id] = DEFINE_PLAYLIST(PL_SYSTEM, name, id, ops)
|
||||
|
||||
static bool sys_pl_most_played_update(struct playlist *playlist,
|
||||
struct track *track)
|
||||
{
|
||||
unsigned int average = track_db_average_plays();
|
||||
if (track->tr_count <= average)
|
||||
return false;
|
||||
return sys_pl_generic_add(playlist, track) || true;
|
||||
}
|
||||
|
||||
static struct sys_playlist sys_most_played = {
|
||||
.spl_playlist = DEFINE_PLAYLIST(PL_SYSTEM, "Most Played"),
|
||||
.spl_init = sys_pl_update_init,
|
||||
.spl_save = sys_pl_save_partial,
|
||||
.spl_load = sys_pl_load_partial,
|
||||
.spl_can_select = playlist_generic_can_select,
|
||||
.spl_add = sys_pl_most_played_add,
|
||||
.spl_remove = playlist_generic_remove_track,
|
||||
.spl_update = sys_pl_most_played_update,
|
||||
.spl_clear = playlist_noop_clear,
|
||||
.spl_set_flag = playlist_generic_set_flag,
|
||||
.spl_sort = playlist_generic_sort,
|
||||
.spl_next = playlist_generic_next,
|
||||
static struct playlist sys_playlists[SYS_PL_NUM_PLAYLISTS] = {
|
||||
SYS_PLAYLIST(SYS_PL_FAVORITES, "Favorites", &favorites_ops),
|
||||
SYS_PLAYLIST(SYS_PL_HIDDEN, "Hidden", &hidden_ops),
|
||||
SYS_PLAYLIST(SYS_PL_QUEUED, "Queued Tracks", &queued_ops),
|
||||
SYS_PLAYLIST(SYS_PL_COLLECTION, "Collection", &collection_ops),
|
||||
SYS_PLAYLIST(SYS_PL_HISTORY, "History", &history_ops),
|
||||
SYS_PLAYLIST(SYS_PL_UNPLAYED, "Unplayed", &dynamic_ops),
|
||||
SYS_PLAYLIST(SYS_PL_MOST_PLAYED, "Most Played", &dynamic_ops),
|
||||
SYS_PLAYLIST(SYS_PL_LEAST_PLAYED, "Least Played", &dynamic_ops),
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Least played tracks playlist operations.
|
||||
*/
|
||||
static bool sys_pl_least_played_add(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
unsigned int average = track_db_average_plays();
|
||||
if (!track->tr_count || track->tr_count > average)
|
||||
return false;
|
||||
return sys_pl_generic_add(playlist, track);
|
||||
}
|
||||
|
||||
static bool sys_pl_least_played_update(struct playlist *playlist,
|
||||
struct track *track)
|
||||
{
|
||||
unsigned int average = track_db_average_plays();
|
||||
if (!track->tr_count || track->tr_count > average)
|
||||
return false;
|
||||
return sys_pl_generic_add(playlist, track) || true;
|
||||
}
|
||||
|
||||
static struct sys_playlist sys_least_played = {
|
||||
.spl_playlist = DEFINE_PLAYLIST(PL_SYSTEM, "Least Played"),
|
||||
.spl_init = sys_pl_update_init,
|
||||
.spl_save = sys_pl_save_partial,
|
||||
.spl_load = sys_pl_load_partial,
|
||||
.spl_can_select = playlist_generic_can_select,
|
||||
.spl_clear = playlist_noop_clear,
|
||||
.spl_add = sys_pl_least_played_add,
|
||||
.spl_remove = playlist_generic_remove_track,
|
||||
.spl_update = sys_pl_least_played_update,
|
||||
.spl_set_flag = playlist_generic_set_flag,
|
||||
.spl_sort = playlist_generic_sort,
|
||||
.spl_next = playlist_generic_next,
|
||||
};
|
||||
|
||||
|
||||
static struct sys_playlist *sys_playlists[SYS_PL_NUM_PLAYLISTS] = {
|
||||
[SYS_PL_FAVORITES] = &sys_favorites,
|
||||
[SYS_PL_HIDDEN] = &sys_hidden,
|
||||
[SYS_PL_QUEUED] = &sys_queued,
|
||||
[SYS_PL_COLLECTION] = &sys_collection,
|
||||
[SYS_PL_HISTORY] = &sys_history,
|
||||
[SYS_PL_UNPLAYED] = &sys_unplayed,
|
||||
[SYS_PL_MOST_PLAYED] = &sys_most_played,
|
||||
[SYS_PL_LEAST_PLAYED] = &sys_least_played,
|
||||
};
|
||||
|
||||
|
||||
static struct sys_playlist * __sys_pl_lookup(const gchar *name)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) {
|
||||
if (string_match(name, sys_playlists[i]->spl_playlist.pl_name))
|
||||
return sys_playlists[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct queue *__sys_pl_queue(enum sys_playlist_t plist)
|
||||
{
|
||||
return &sys_playlists[plist]->spl_playlist.pl_queue;
|
||||
}
|
||||
|
||||
static void __sys_pl_save()
|
||||
{
|
||||
struct sys_playlist *sys_pl;
|
||||
unsigned int i;
|
||||
|
||||
if (!file_open(&sys_pl_file, OPEN_WRITE))
|
||||
return;
|
||||
|
||||
file_writef(&sys_pl_file, "%u\n", SYS_PL_NUM_PLAYLISTS);
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) {
|
||||
sys_pl = sys_playlists[i];
|
||||
sys_pl->spl_save(&sys_pl->spl_playlist, &sys_pl_file);
|
||||
}
|
||||
|
||||
file_close(&sys_pl_file);
|
||||
}
|
||||
|
||||
static bool __sys_pl_update_save()
|
||||
{
|
||||
__sys_pl_save();
|
||||
pl_system_save();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __sys_pl_load()
|
||||
{
|
||||
struct playlist *playlist;
|
||||
unsigned int i, n;
|
||||
gchar *name;
|
||||
|
||||
if (!file_open(&sys_file, OPEN_READ))
|
||||
return true;
|
||||
|
||||
n = file_readu(&sys_file);
|
||||
for (i = 0; i < n; i++) {
|
||||
file_readu(&sys_file);
|
||||
name = file_readl(&sys_file);
|
||||
if (string_match(name, "Banned")) {
|
||||
g_free(name);
|
||||
name = g_strdup("Hidden");
|
||||
}
|
||||
playlist = pl_system_lookup(name);
|
||||
g_free(name);
|
||||
|
||||
if (playlist)
|
||||
playlist_generic_load(playlist, &sys_file, PL_SAVE_TRACKS);
|
||||
}
|
||||
|
||||
file_close(&sys_file);
|
||||
file_remove(&sys_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __sys_pl_load_new()
|
||||
{
|
||||
struct sys_playlist *sys_pl;
|
||||
unsigned int i, n;
|
||||
struct playlist *playlist;
|
||||
unsigned int i, n, load;
|
||||
gchar *name;
|
||||
|
||||
if (!file_open(&sys_pl_file, OPEN_READ)) {
|
||||
|
@ -429,213 +303,152 @@ static bool __sys_pl_load_new()
|
|||
return true;
|
||||
}
|
||||
|
||||
file_readf(&sys_pl_file, "%u\n", &n);
|
||||
n = file_readu(&sys_pl_file);
|
||||
for (i = 0; i < n; i++) {
|
||||
name = file_readl(&sys_pl_file);
|
||||
sys_pl = __sys_pl_lookup(name);
|
||||
if (sys_pl)
|
||||
sys_pl->spl_load(&sys_pl->spl_playlist, &sys_pl_file);
|
||||
load = PL_SAVE_METADATA;
|
||||
name = file_readl(&sys_pl_file);
|
||||
|
||||
if (string_match(name, "Banned")) {
|
||||
g_free(name);
|
||||
name = g_strdup("Hidden");
|
||||
}
|
||||
|
||||
playlist = pl_system_lookup(name);
|
||||
g_free(name);
|
||||
|
||||
switch (i) {
|
||||
case SYS_PL_FAVORITES:
|
||||
case SYS_PL_HIDDEN:
|
||||
case SYS_PL_QUEUED:
|
||||
load = PL_SAVE_ALL;
|
||||
}
|
||||
|
||||
playlist_generic_load(playlist, &sys_pl_file, load);
|
||||
}
|
||||
|
||||
file_close(&sys_pl_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __sys_pl_load()
|
||||
{
|
||||
struct sys_playlist *plist;
|
||||
unsigned int i, n;
|
||||
gchar *name;
|
||||
|
||||
if (!file_open(&sys_file, OPEN_READ))
|
||||
return true;
|
||||
|
||||
file_readf(&sys_file, "%u\n", &n);
|
||||
for (i = 0; i < n; i++) {
|
||||
file_readf(&sys_file, "%*u %m[^\n]\n", &name);
|
||||
if (string_match(name, "Banned")) {
|
||||
g_free(name);
|
||||
name = g_strdup("Hidden");
|
||||
}
|
||||
|
||||
plist = __sys_pl_lookup(name);
|
||||
if (plist)
|
||||
queue_load_tracks(&plist->spl_playlist.pl_queue, &sys_file);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
file_close(&sys_file);
|
||||
file_remove(&sys_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void pl_system_save(void)
|
||||
{
|
||||
__sys_pl_save();
|
||||
struct playlist *playlist;
|
||||
unsigned int i, save;
|
||||
|
||||
if (!file_open(&sys_pl_file, OPEN_WRITE))
|
||||
return;
|
||||
|
||||
file_writef(&sys_pl_file, "%u\n", SYS_PL_NUM_PLAYLISTS);
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) {
|
||||
save = PL_SAVE_METADATA;
|
||||
playlist = pl_system_get(i);
|
||||
|
||||
switch (i) {
|
||||
case SYS_PL_FAVORITES:
|
||||
case SYS_PL_HIDDEN:
|
||||
case SYS_PL_QUEUED:
|
||||
save = PL_SAVE_ALL;
|
||||
}
|
||||
|
||||
file_writef(&sys_pl_file, "%s\n", playlist->pl_name);
|
||||
playlist_generic_save(playlist, &sys_pl_file, save);
|
||||
|
||||
}
|
||||
|
||||
file_close(&sys_pl_file);
|
||||
}
|
||||
|
||||
static struct queue *pl_system_get_queue(const gchar *name)
|
||||
{
|
||||
struct sys_playlist *sys_pl = __sys_pl_lookup(name);
|
||||
return sys_pl ? &sys_pl->spl_playlist.pl_queue : NULL;
|
||||
}
|
||||
|
||||
static unsigned int pl_system_get_id(const gchar *name)
|
||||
static struct playlist *pl_system_lookup(const gchar *name)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) {
|
||||
if (string_match(name, sys_playlists[i]->spl_playlist.pl_name))
|
||||
return i;
|
||||
if (string_match(name, pl_system_get(i)->pl_name))
|
||||
return pl_system_get(i);
|
||||
}
|
||||
return SYS_PL_NUM_PLAYLISTS;
|
||||
}
|
||||
|
||||
static gchar *pl_system_get_name(unsigned int id)
|
||||
{
|
||||
if (id < SYS_PL_NUM_PLAYLISTS)
|
||||
return g_strdup(sys_playlists[id]->spl_playlist.pl_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool pl_system_can_select(const gchar *name)
|
||||
static struct playlist *pl_system_get(unsigned int id)
|
||||
{
|
||||
struct sys_playlist *sys_pl = __sys_pl_lookup(name);
|
||||
return sys_pl ? sys_pl->spl_can_select(&sys_pl->spl_playlist) : false;
|
||||
return (id < SYS_PL_NUM_PLAYLISTS) ? &sys_playlists[id] : NULL;
|
||||
}
|
||||
|
||||
static bool pl_system_new(const gchar *name)
|
||||
static void pl_system_played(struct track *track)
|
||||
{
|
||||
return false;
|
||||
unsigned int i;
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++)
|
||||
playlist_generic_update(pl_system_get(i), track);
|
||||
|
||||
sys_pl_update(pl_system_lookup("Unplayed"));
|
||||
sys_pl_update(pl_system_lookup("Most Played"));
|
||||
sys_pl_update(pl_system_lookup("Least Played"));
|
||||
}
|
||||
|
||||
static bool pl_system_delete(const gchar *name)
|
||||
static void pl_system_selected(struct track *track)
|
||||
{
|
||||
struct sys_playlist *sys_pl = __sys_pl_lookup(name);
|
||||
if (sys_pl) {
|
||||
sys_pl->spl_clear(&sys_pl->spl_playlist);
|
||||
__sys_pl_save();
|
||||
}
|
||||
return false; /* Don't remove the playlist from the sidebar. */
|
||||
}
|
||||
unsigned int i;
|
||||
|
||||
static bool pl_system_add_track(const gchar *name, struct track *track)
|
||||
{
|
||||
struct sys_playlist *sys_pl = __sys_pl_lookup(name);
|
||||
bool ret = false;
|
||||
|
||||
if (sys_pl) {
|
||||
ret = sys_pl->spl_add(&sys_pl->spl_playlist, track);
|
||||
if (ret)
|
||||
__sys_pl_save();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool pl_system_remove_track(const gchar *name, struct track *track)
|
||||
{
|
||||
struct sys_playlist *sys_pl = __sys_pl_lookup(name);
|
||||
bool ret = false;
|
||||
|
||||
if (sys_pl) {
|
||||
ret = sys_pl->spl_remove(&sys_pl->spl_playlist, track);
|
||||
if (ret)
|
||||
__sys_pl_save();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pl_system_update(const gchar *name)
|
||||
{
|
||||
struct sys_playlist *sys_pl = __sys_pl_lookup(name);
|
||||
if (sys_pl)
|
||||
playlist_generic_update(&sys_pl->spl_playlist, sys_pl->spl_update);
|
||||
}
|
||||
|
||||
static void pl_system_set_flag(const gchar *name, enum queue_flags flag,
|
||||
bool enabled)
|
||||
{
|
||||
struct sys_playlist *sys_pl = __sys_pl_lookup(name);
|
||||
if (sys_pl) {
|
||||
sys_pl->spl_set_flag(&sys_pl->spl_playlist, flag, enabled);
|
||||
__sys_pl_save();
|
||||
}
|
||||
}
|
||||
|
||||
static void pl_system_sort(const gchar *name, enum compare_t sort, bool reset)
|
||||
{
|
||||
struct sys_playlist *sys_pl = __sys_pl_lookup(name);
|
||||
if (sys_pl) {
|
||||
sys_pl->spl_sort(&sys_pl->spl_playlist, sort, reset);
|
||||
__sys_pl_save();
|
||||
}
|
||||
}
|
||||
|
||||
static struct track *pl_system_next(const gchar *name)
|
||||
{
|
||||
struct sys_playlist *sys_pl = __sys_pl_lookup(name);
|
||||
struct track *track = NULL;;
|
||||
if (sys_pl) {
|
||||
track = sys_pl->spl_next(&sys_pl->spl_playlist);
|
||||
__sys_pl_save();
|
||||
}
|
||||
return track;
|
||||
sys_pl_queued_remove(pl_system_get(SYS_PL_QUEUED), track);
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++)
|
||||
playlist_generic_update(pl_system_get(i), track);
|
||||
}
|
||||
|
||||
|
||||
struct playlist_type pl_system = {
|
||||
.pl_save = pl_system_save,
|
||||
.pl_get_queue = pl_system_get_queue,
|
||||
.pl_get_id = pl_system_get_id,
|
||||
.pl_get_name = pl_system_get_name,
|
||||
.pl_can_select = pl_system_can_select,
|
||||
.pl_new = pl_system_new,
|
||||
.pl_delete = pl_system_delete,
|
||||
.pl_add_track = pl_system_add_track,
|
||||
.pl_remove_track = pl_system_remove_track,
|
||||
.pl_update = pl_system_update,
|
||||
.pl_set_flag = pl_system_set_flag,
|
||||
.pl_sort = pl_system_sort,
|
||||
.pl_next = pl_system_next,
|
||||
.pl_save = pl_system_save,
|
||||
.pl_lookup = pl_system_lookup,
|
||||
.pl_get = pl_system_get,
|
||||
.pl_played = pl_system_played,
|
||||
.pl_selected = pl_system_selected,
|
||||
};
|
||||
|
||||
|
||||
void pl_system_init(struct queue_ops *ops)
|
||||
void pl_system_init(void)
|
||||
{
|
||||
struct sys_playlist *sys_pl;
|
||||
struct playlist *playlist;
|
||||
unsigned int i;
|
||||
|
||||
idle_schedule(IDLE_SYNC, __sys_pl_load_new, NULL);
|
||||
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) {
|
||||
sys_pl = sys_playlists[i];
|
||||
sys_pl->spl_init(&sys_pl->spl_playlist, Q_REPEAT, ops);
|
||||
playlist = pl_system_get(i);
|
||||
|
||||
switch (i) {
|
||||
case SYS_PL_QUEUED:
|
||||
case SYS_PL_HISTORY:
|
||||
playlist_generic_init(playlist, 0);
|
||||
break;
|
||||
case SYS_PL_COLLECTION:
|
||||
case SYS_PL_UNPLAYED:
|
||||
case SYS_PL_MOST_PLAYED:
|
||||
case SYS_PL_LEAST_PLAYED:
|
||||
sys_pl_update(playlist);
|
||||
case SYS_PL_FAVORITES:
|
||||
case SYS_PL_HIDDEN:
|
||||
playlist_generic_init(playlist, 4, COMPARE_ARTIST,
|
||||
COMPARE_YEAR, COMPARE_ALBUM, COMPARE_TRACK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pl_system_deinit()
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++)
|
||||
queue_deinit(__sys_pl_queue(i));
|
||||
for (unsigned int i = 0; i < SYS_PL_NUM_PLAYLISTS; i++)
|
||||
playlist_generic_deinit(pl_system_get(i));
|
||||
}
|
||||
|
||||
void pl_system_new_track(struct track *track)
|
||||
{
|
||||
pl_system_add_track("Collection", track);
|
||||
pl_system_add_track("Unplayed", track);
|
||||
playlist_generic_add(pl_system_lookup("Collection"), track);
|
||||
playlist_generic_add(pl_system_lookup("Unplayed"), track);
|
||||
}
|
||||
|
||||
void pl_system_delete_track(struct track *track)
|
||||
{
|
||||
struct sys_playlist *sys_pl;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) {
|
||||
sys_pl = sys_playlists[i];
|
||||
sys_pl->spl_remove(&sys_pl->spl_playlist, track);
|
||||
}
|
||||
for (unsigned int i = 0; i < SYS_PL_NUM_PLAYLISTS; i++)
|
||||
playlist_generic_remove(pl_system_get(i), track);
|
||||
}
|
||||
|
|
|
@ -3,29 +3,31 @@
|
|||
*/
|
||||
#include <core/playlists/user.h>
|
||||
|
||||
static struct queue_ops *user_pl_ops = NULL;
|
||||
static struct database user_db;
|
||||
static struct database user_db;
|
||||
static struct playlist_ops user_ops;
|
||||
|
||||
static struct user_playlist *__user_db_alloc(gchar *name)
|
||||
static struct user_playlist *__user_db_alloc(gchar *name, unsigned int index)
|
||||
{
|
||||
struct user_playlist *playlist = g_malloc(sizeof(struct user_playlist));
|
||||
|
||||
dbe_init(&playlist->pl_dbe, playlist);
|
||||
playlist->pl_playlist.pl_name = name;
|
||||
playlist->pl_playlist.pl_type = PL_USER;
|
||||
playlist_generic_init(&playlist->pl_playlist, Q_REPEAT, user_pl_ops);
|
||||
playlist->pl_playlist.pl_id = index;
|
||||
playlist->pl_playlist.pl_ops = &user_ops;
|
||||
playlist_generic_init(&playlist->pl_playlist, 0);
|
||||
|
||||
return playlist;
|
||||
}
|
||||
|
||||
static struct db_entry *user_db_alloc(const gchar *name)
|
||||
static struct db_entry *user_db_alloc(const gchar *name, unsigned int index)
|
||||
{
|
||||
return &__user_db_alloc(g_strdup(name))->pl_dbe;
|
||||
return &__user_db_alloc(g_strdup(name), index)->pl_dbe;
|
||||
}
|
||||
|
||||
static void user_db_free(struct db_entry *dbe)
|
||||
{
|
||||
queue_deinit(&USER_PLAYLIST(dbe)->pl_playlist.pl_queue);
|
||||
playlist_generic_deinit(&USER_PLAYLIST(dbe)->pl_playlist);
|
||||
g_free(USER_PLAYLIST(dbe)->pl_playlist.pl_name);
|
||||
g_free(USER_PLAYLIST(dbe));
|
||||
}
|
||||
|
@ -35,17 +37,12 @@ static gchar *user_db_key(struct db_entry *dbe)
|
|||
return g_strdup(USER_PLAYLIST(dbe)->pl_playlist.pl_name);
|
||||
}
|
||||
|
||||
static struct db_entry *user_db_read(struct file *file)
|
||||
static struct db_entry *user_db_read(struct file *file, unsigned int index)
|
||||
{
|
||||
gchar *name = file_readl(file);
|
||||
struct user_playlist *playlist = __user_db_alloc(name);
|
||||
|
||||
queue_load_flags(&playlist->pl_playlist.pl_queue, file, true);
|
||||
queue_load_tracks(&playlist->pl_playlist.pl_queue, file);
|
||||
queue_iter_set(&playlist->pl_playlist.pl_queue,
|
||||
&playlist->pl_playlist.pl_queue.q_cur,
|
||||
playlist->pl_playlist.pl_queue.q_cur.it_pos);
|
||||
struct user_playlist *playlist = __user_db_alloc(name, index);
|
||||
|
||||
playlist_generic_load(&playlist->pl_playlist, file, PL_SAVE_ALL);
|
||||
return &playlist->pl_dbe;
|
||||
}
|
||||
|
||||
|
@ -54,8 +51,7 @@ static void user_db_write(struct file *file, struct db_entry *dbe)
|
|||
struct playlist *playlist = &USER_PLAYLIST(dbe)->pl_playlist;
|
||||
|
||||
file_writef(file, "%s\n", playlist->pl_name);
|
||||
queue_save_flags(&playlist->pl_queue, file, true);
|
||||
queue_save_tracks(&playlist->pl_queue, file);
|
||||
playlist_generic_save(playlist, file, PL_SAVE_ALL);
|
||||
}
|
||||
|
||||
|
||||
|
@ -68,51 +64,9 @@ static const struct db_ops user_db_ops = {
|
|||
};
|
||||
|
||||
|
||||
static struct playlist *__user_pl_lookup(const gchar *name)
|
||||
static bool pl_user_delete(struct playlist *playlist)
|
||||
{
|
||||
struct db_entry *dbe = db_get(&user_db, name);
|
||||
return dbe ? &USER_PLAYLIST(dbe)->pl_playlist : NULL;
|
||||
}
|
||||
|
||||
static void pl_user_save(void)
|
||||
{
|
||||
db_save(&user_db);
|
||||
}
|
||||
|
||||
static struct queue *pl_user_get_queue(const gchar *name)
|
||||
{
|
||||
struct playlist *playlist = __user_pl_lookup(name);
|
||||
return playlist ? &playlist->pl_queue : NULL;
|
||||
}
|
||||
|
||||
static unsigned int pl_user_get_id(const gchar *name)
|
||||
{
|
||||
struct db_entry *dbe = db_get(&user_db, name);
|
||||
return dbe ? dbe->dbe_index : -1;
|
||||
}
|
||||
|
||||
static gchar *pl_user_get_name(unsigned int id)
|
||||
{
|
||||
struct db_entry *dbe = db_at(&user_db, id);
|
||||
return dbe ? g_strdup(USER_PLAYLIST(dbe)->pl_playlist.pl_name) : NULL;
|
||||
}
|
||||
|
||||
static bool pl_user_can_select(const gchar *name)
|
||||
{
|
||||
return db_get(&user_db, name) != NULL;
|
||||
}
|
||||
|
||||
static bool pl_user_new(const gchar *name)
|
||||
{
|
||||
if (db_get(&user_db, name))
|
||||
return false;
|
||||
db_insert(&user_db, name);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pl_user_delete(const gchar *name)
|
||||
{
|
||||
struct db_entry *dbe = db_get(&user_db, name);
|
||||
struct db_entry *dbe = db_get(&user_db, playlist->pl_name);
|
||||
if (dbe) {
|
||||
db_remove(&user_db, dbe);
|
||||
db_defrag(&user_db);
|
||||
|
@ -120,82 +74,65 @@ static bool pl_user_delete(const gchar *name)
|
|||
return dbe != NULL;
|
||||
}
|
||||
|
||||
static bool pl_user_add(const gchar *name, struct track *track)
|
||||
|
||||
static struct playlist_ops user_ops = {
|
||||
.pl_add = playlist_generic_add,
|
||||
.pl_can_select = playlist_generic_can_select,
|
||||
.pl_delete = pl_user_delete,
|
||||
.pl_remove = playlist_generic_remove,
|
||||
.pl_set_random = playlist_generic_set_random,
|
||||
.pl_sort = playlist_generic_sort,
|
||||
.pl_rearrange = playlist_generic_rearrange,
|
||||
};
|
||||
|
||||
|
||||
static void pl_user_save(void)
|
||||
{
|
||||
struct playlist *playlist = __user_pl_lookup(name);
|
||||
bool ret = false;
|
||||
|
||||
if (playlist) {
|
||||
ret = playlist_generic_add_track(playlist, track);
|
||||
if (ret)
|
||||
pl_user_save();
|
||||
}
|
||||
|
||||
return ret;
|
||||
db_save(&user_db);
|
||||
}
|
||||
|
||||
static bool pl_user_remove(const gchar *name, struct track *track)
|
||||
static struct playlist *pl_user_lookup(const gchar *name)
|
||||
{
|
||||
struct playlist *playlist = __user_pl_lookup(name);
|
||||
bool ret = false;
|
||||
|
||||
if (playlist) {
|
||||
ret = playlist_generic_remove_track(playlist, track);
|
||||
if (ret)
|
||||
pl_user_save();
|
||||
}
|
||||
|
||||
return ret;
|
||||
struct db_entry *dbe = db_get(&user_db, name);
|
||||
return dbe ? &USER_PLAYLIST(dbe)->pl_playlist : NULL;
|
||||
}
|
||||
|
||||
static void pl_user_update(const gchar *name)
|
||||
static struct playlist *pl_user_get(unsigned int id)
|
||||
{
|
||||
struct db_entry *dbe = db_at(&user_db, id);
|
||||
return dbe ? &USER_PLAYLIST(dbe)->pl_playlist : NULL;
|
||||
}
|
||||
|
||||
static void pl_user_set_flag(const gchar *name, enum queue_flags flag,
|
||||
bool enabled)
|
||||
static struct playlist *pl_user_new(const gchar *name)
|
||||
{
|
||||
struct playlist *playlist = __user_pl_lookup(name);
|
||||
playlist_generic_set_flag(playlist, flag, enabled);
|
||||
pl_user_save();
|
||||
struct db_entry *dbe;
|
||||
|
||||
if (db_get(&user_db, name))
|
||||
return NULL;
|
||||
dbe = db_insert(&user_db, name);
|
||||
return dbe ? &USER_PLAYLIST(dbe)->pl_playlist : NULL;
|
||||
}
|
||||
|
||||
static void pl_user_sort(const gchar *name, enum compare_t sort, bool reset)
|
||||
static void pl_user_played(struct track *track)
|
||||
{
|
||||
struct playlist *playlist = __user_pl_lookup(name);
|
||||
playlist_generic_sort(playlist, sort, reset);
|
||||
pl_user_save();
|
||||
}
|
||||
|
||||
static struct track *pl_user_next(const gchar *name)
|
||||
{
|
||||
struct playlist *playlist = __user_pl_lookup(name);
|
||||
struct track *track = playlist_generic_next(playlist);
|
||||
pl_user_save();
|
||||
return track;
|
||||
struct db_entry *dbe, *next;
|
||||
db_for_each(dbe, next, &user_db)
|
||||
playlist_generic_update(&USER_PLAYLIST(dbe)->pl_playlist, track);
|
||||
}
|
||||
|
||||
|
||||
struct playlist_type pl_user = {
|
||||
.pl_save = pl_user_save,
|
||||
.pl_get_queue = pl_user_get_queue,
|
||||
.pl_get_id = pl_user_get_id,
|
||||
.pl_get_name = pl_user_get_name,
|
||||
.pl_can_select = pl_user_can_select,
|
||||
.pl_new = pl_user_new,
|
||||
.pl_delete = pl_user_delete,
|
||||
.pl_add_track = pl_user_add,
|
||||
.pl_remove_track = pl_user_remove,
|
||||
.pl_update = pl_user_update,
|
||||
.pl_set_flag = pl_user_set_flag,
|
||||
.pl_sort = pl_user_sort,
|
||||
.pl_next = pl_user_next,
|
||||
.pl_save = pl_user_save,
|
||||
.pl_lookup = pl_user_lookup,
|
||||
.pl_get = pl_user_get,
|
||||
.pl_new = pl_user_new,
|
||||
.pl_played = pl_user_played,
|
||||
.pl_selected = pl_user_played,
|
||||
};
|
||||
|
||||
|
||||
void pl_user_init(struct queue_ops *ops)
|
||||
void pl_user_init(void)
|
||||
{
|
||||
user_pl_ops = ops;
|
||||
db_init(&user_db, "playlist.user", true, &user_db_ops, 0);
|
||||
db_load(&user_db);
|
||||
}
|
||||
|
@ -209,3 +146,33 @@ struct database *pl_user_db_get()
|
|||
{
|
||||
return &user_db;
|
||||
}
|
||||
|
||||
void pl_user_delete_track(struct track *track)
|
||||
{
|
||||
struct db_entry *dbe, *next;
|
||||
struct playlist *playlist;
|
||||
|
||||
db_for_each(dbe, next, &user_db) {
|
||||
playlist = &USER_PLAYLIST(dbe)->pl_playlist;
|
||||
playlist_generic_remove(playlist, track);
|
||||
}
|
||||
}
|
||||
|
||||
bool pl_user_rename(struct playlist *playlist, const gchar *name)
|
||||
{
|
||||
struct db_entry *dbe;
|
||||
|
||||
if (!playlist || db_get(&user_db, name))
|
||||
return false;
|
||||
|
||||
dbe = db_get(&user_db, playlist->pl_name);
|
||||
if (!dbe)
|
||||
return false;
|
||||
|
||||
g_free(playlist->pl_name);
|
||||
playlist->pl_name = g_strdup(name);
|
||||
db_rekey(&user_db, dbe);
|
||||
|
||||
pl_user_save();
|
||||
return true;
|
||||
}
|
||||
|
|
385
core/queue.c
385
core/queue.c
|
@ -1,385 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/queue.h>
|
||||
#include <core/string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
static int track_less_than(const void *a, const void *b, void *data)
|
||||
{
|
||||
struct track *lhs = (struct track *)a;
|
||||
struct track *rhs = (struct track *)b;
|
||||
GSList *cur = (GSList *)data;
|
||||
int res, field;
|
||||
|
||||
while (cur) {
|
||||
field = GPOINTER_TO_INT(cur->data);
|
||||
if (field > 0)
|
||||
res = track_compare(lhs, rhs, field);
|
||||
else
|
||||
res = track_compare(rhs, lhs, abs(field));
|
||||
if (res == 0) {
|
||||
cur = g_slist_next(cur);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline void *__queue_init(struct queue *queue, void *data)
|
||||
{
|
||||
if (queue->q_ops)
|
||||
return queue->q_ops->qop_init(queue, data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void __queue_deinit(struct queue *queue)
|
||||
{
|
||||
if (queue->q_ops)
|
||||
queue->q_ops->qop_deinit(queue);
|
||||
}
|
||||
|
||||
static inline unsigned int __queue_added(struct queue *queue,
|
||||
struct track *track,
|
||||
unsigned int pos)
|
||||
{
|
||||
queue->q_length += track->tr_length;
|
||||
if (queue->q_ops)
|
||||
queue->q_ops->qop_added(queue, pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
static inline unsigned int __queue_add_head(struct queue *queue,
|
||||
struct track *track)
|
||||
{
|
||||
g_queue_push_head(&queue->q_tracks, track);
|
||||
return __queue_added(queue, track, 0);
|
||||
}
|
||||
|
||||
static inline unsigned int __queue_add_tail(struct queue *queue,
|
||||
struct track *track)
|
||||
{
|
||||
g_queue_push_tail(&queue->q_tracks, track);
|
||||
return __queue_added(queue, track, queue_size(queue) - 1);
|
||||
}
|
||||
|
||||
static inline unsigned int __queue_add_sorted(struct queue *queue,
|
||||
struct track *track)
|
||||
{
|
||||
struct queue_iter it;
|
||||
|
||||
queue_for_each(queue, &it) {
|
||||
if (track_less_than(queue_iter_val(&it), track, queue->q_sort) > 0) {
|
||||
g_queue_insert_before(&queue->q_tracks, it.it_iter, track);
|
||||
return __queue_added(queue, track, it.it_pos);
|
||||
}
|
||||
}
|
||||
return __queue_add_tail(queue, track);
|
||||
}
|
||||
|
||||
static inline bool __queue_erase(struct queue *queue, struct queue_iter *it)
|
||||
{
|
||||
struct track *track = queue_iter_val(it);
|
||||
|
||||
if (queue->q_ops)
|
||||
return queue->q_ops->qop_erase(queue, track);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void __queue_remove(struct queue *queue, struct queue_iter *it)
|
||||
{
|
||||
struct track *track = queue_iter_val(it);
|
||||
unsigned int pos = it->it_pos;
|
||||
GList *link = it->it_iter;
|
||||
|
||||
queue_iter_prev(it);
|
||||
g_queue_delete_link(&queue->q_tracks, link);
|
||||
|
||||
queue->q_length -= track->tr_length;
|
||||
if (queue->q_ops)
|
||||
queue->q_ops->qop_removed(queue, pos);
|
||||
}
|
||||
|
||||
static inline void __queue_clear(struct queue *queue, unsigned int n)
|
||||
{
|
||||
queue->q_length = 0;
|
||||
if (queue->q_ops)
|
||||
queue->q_ops->qop_cleared(queue, n);
|
||||
}
|
||||
|
||||
static inline void __queue_updated(struct queue *queue, unsigned int pos)
|
||||
{
|
||||
if (queue->q_ops)
|
||||
queue->q_ops->qop_updated(queue, pos);
|
||||
}
|
||||
|
||||
static inline struct track *__queue_selected(struct queue *queue, unsigned int pos)
|
||||
{
|
||||
struct track *track = queue_iter_val(&queue->q_cur);
|
||||
|
||||
if (queue_has_flag(queue, Q_REPEAT) == false)
|
||||
__queue_remove(queue, &queue->q_cur);
|
||||
else
|
||||
__queue_updated(queue, pos);
|
||||
return track;
|
||||
}
|
||||
|
||||
static inline void __queue_save(struct queue *queue, enum queue_flags flag)
|
||||
{
|
||||
if (queue->q_ops && queue_has_flag(queue, flag))
|
||||
queue->q_ops->qop_save(queue, flag);
|
||||
}
|
||||
|
||||
void queue_init(struct queue *queue, unsigned int flags,
|
||||
const struct queue_ops *ops, void *data)
|
||||
{
|
||||
queue->q_flags = flags;
|
||||
queue->q_length = 0;
|
||||
queue->q_sort = NULL;
|
||||
queue->q_ops = ops;
|
||||
|
||||
g_queue_init(&queue->q_tracks);
|
||||
queue_iter_init(queue, &queue->q_cur);
|
||||
|
||||
queue->q_private = __queue_init(queue, data);
|
||||
}
|
||||
|
||||
void queue_deinit(struct queue *queue)
|
||||
{
|
||||
queue_clear(queue);
|
||||
__queue_deinit(queue);
|
||||
g_slist_free(queue->q_sort);
|
||||
queue->q_sort = NULL;
|
||||
}
|
||||
|
||||
void queue_save_flags(struct queue *queue, struct file *file, bool iter)
|
||||
{
|
||||
GSList *cur = queue->q_sort;
|
||||
int field;
|
||||
|
||||
if (iter)
|
||||
file_writef(file, "%u ", queue->q_cur.it_pos);
|
||||
|
||||
file_writef(file, "%u %u", queue->q_flags, g_slist_length(cur));
|
||||
while (cur) {
|
||||
field = GPOINTER_TO_INT(cur->data);
|
||||
file_writef(file, " %u %d", abs(field) - 1, field > 0);
|
||||
cur = g_slist_next(cur);
|
||||
}
|
||||
file_writef(file, "\n");
|
||||
}
|
||||
|
||||
void queue_save_tracks(struct queue *queue, struct file *file)
|
||||
{
|
||||
struct queue_iter it;
|
||||
|
||||
file_writef(file, "%u", queue_size(queue));
|
||||
queue_for_each(queue, &it)
|
||||
file_writef(file, " %u", track_index(queue_iter_val(&it)));
|
||||
file_writef(file, "\n");
|
||||
}
|
||||
|
||||
void queue_load_flags(struct queue *queue, struct file *file, bool iter)
|
||||
{
|
||||
unsigned int flags, n, i;
|
||||
int field, ascending;
|
||||
unsigned int it = 0;
|
||||
gchar *line;
|
||||
|
||||
if (iter)
|
||||
file_readf(file, "%u", &it);
|
||||
|
||||
file_readf(file, "%u %u", &flags, &n);
|
||||
for (i = 0; i < n; i++) {
|
||||
file_readf(file, "%u %d", &field, &ascending);
|
||||
queue_sort(queue, field + 1, (i == 0) ? true : false);
|
||||
if (ascending == false)
|
||||
queue_sort(queue, field + 1, false);
|
||||
}
|
||||
|
||||
queue->q_flags |= flags;
|
||||
queue->q_cur.it_pos = it;
|
||||
|
||||
if (file_readf(file, "%m\n", &line))
|
||||
g_free(line);
|
||||
}
|
||||
|
||||
void queue_load_tracks(struct queue *queue, struct file *file)
|
||||
{
|
||||
unsigned int i, n, t;
|
||||
gchar *line;
|
||||
|
||||
file_readf(file, "%u ", &n);
|
||||
for (i = 0; i < n; i++) {
|
||||
file_readf(file, "%u", &t);
|
||||
queue_add(queue, track_get(t));
|
||||
}
|
||||
|
||||
if (file_readf(file, "%m\n", &line))
|
||||
g_free(line);
|
||||
}
|
||||
|
||||
void queue_set_flag(struct queue *queue, enum queue_flags flag)
|
||||
{
|
||||
queue->q_flags |= flag;
|
||||
__queue_save(queue, Q_SAVE_FLAGS);
|
||||
}
|
||||
|
||||
void queue_unset_flag(struct queue *queue, enum queue_flags flag)
|
||||
{
|
||||
if (!queue_has_flag(queue, flag))
|
||||
return;
|
||||
queue->q_flags &= ~flag;
|
||||
if (flag == Q_ADD_FRONT) {
|
||||
queue_resort(queue);
|
||||
queue_iter_set(queue, &queue->q_cur, queue->q_cur.it_pos);
|
||||
}
|
||||
__queue_save(queue, Q_SAVE_FLAGS);
|
||||
}
|
||||
|
||||
unsigned int queue_add(struct queue *queue, struct track *track)
|
||||
{
|
||||
if (queue_has_flag(queue, Q_ADD_FRONT))
|
||||
return __queue_add_head(queue, track);
|
||||
if (queue->q_sort)
|
||||
return __queue_add_sorted(queue, track);
|
||||
return __queue_add_tail(queue, track);
|
||||
}
|
||||
|
||||
void queue_erase(struct queue *queue, unsigned int index)
|
||||
{
|
||||
struct queue_iter it;
|
||||
|
||||
queue_iter_set(queue, &it, index);
|
||||
if (__queue_erase(queue, &it))
|
||||
__queue_remove(queue, &it);
|
||||
}
|
||||
|
||||
void queue_remove(struct queue *queue, unsigned int index)
|
||||
{
|
||||
struct queue_iter it;
|
||||
|
||||
queue_iter_set(queue, &it, index);
|
||||
__queue_remove(queue, &it);
|
||||
}
|
||||
|
||||
unsigned int queue_remove_all(struct queue *queue, struct track *track)
|
||||
{
|
||||
unsigned int count = 0;
|
||||
struct queue_iter it;
|
||||
|
||||
while (queue_at(queue, 0) == track) {
|
||||
queue_remove(queue, 0);
|
||||
count++;
|
||||
}
|
||||
|
||||
queue_for_each(queue, &it) {
|
||||
if (queue_iter_val(&it) == track) {
|
||||
__queue_remove(queue, &it);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void queue_clear(struct queue *queue)
|
||||
{
|
||||
unsigned int n = queue_size(queue);
|
||||
|
||||
g_queue_clear(&queue->q_tracks);
|
||||
__queue_clear(queue, n);
|
||||
}
|
||||
|
||||
bool queue_has(struct queue *queue, struct track *track)
|
||||
{
|
||||
struct queue_iter it;
|
||||
|
||||
queue_for_each(queue, &it) {
|
||||
if (queue_iter_val(&it) == track)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void queue_updated(struct queue *queue, struct track *track)
|
||||
{
|
||||
struct queue_iter it;
|
||||
|
||||
queue_for_each(queue, &it) {
|
||||
if (queue_iter_val(&it) == track)
|
||||
__queue_updated(queue, it.it_pos);
|
||||
}
|
||||
}
|
||||
|
||||
struct track *queue_selected(struct queue *queue, unsigned int index)
|
||||
{
|
||||
if (index > queue_size(queue))
|
||||
return NULL;
|
||||
if (queue->q_cur.it_pos != index)
|
||||
queue_iter_set(queue, &queue->q_cur, index);
|
||||
return __queue_selected(queue, index);
|
||||
}
|
||||
|
||||
struct track *queue_next(struct queue *queue)
|
||||
{
|
||||
unsigned int pos, size = queue_size(queue);
|
||||
|
||||
if (!queue_has_flag(queue, Q_ENABLED))
|
||||
return NULL;
|
||||
else if (size == 0)
|
||||
return NULL;
|
||||
|
||||
if (size == 1)
|
||||
queue_iter_set(queue, &queue->q_cur, 0);
|
||||
else if (queue_has_flag(queue, Q_RANDOM)) {
|
||||
pos = g_random_int_range(1, (size < 15) ? size : size / 3);
|
||||
pos += queue->q_cur.it_pos;
|
||||
queue_iter_set(queue, &queue->q_cur, pos % size);
|
||||
} else {
|
||||
queue_iter_next(&queue->q_cur);
|
||||
if (!queue->q_cur.it_iter)
|
||||
queue_iter_set(queue, &queue->q_cur, 0);
|
||||
}
|
||||
|
||||
return __queue_selected(queue, queue->q_cur.it_pos);
|
||||
}
|
||||
|
||||
void queue_resort(struct queue *queue)
|
||||
{
|
||||
g_queue_sort(&queue->q_tracks, track_less_than, queue->q_sort);
|
||||
|
||||
for (unsigned int i = 0; i < queue_size(queue); i++)
|
||||
__queue_updated(queue, i);
|
||||
}
|
||||
|
||||
void queue_sort(struct queue *queue, enum compare_t sort, bool reset)
|
||||
{
|
||||
GSList *cur = NULL;
|
||||
int field;
|
||||
|
||||
if (queue_has_flag(queue, Q_NO_SORT))
|
||||
return;
|
||||
if (reset) {
|
||||
g_slist_free(queue->q_sort);
|
||||
queue->q_sort = NULL;
|
||||
}
|
||||
|
||||
cur = queue->q_sort;
|
||||
while (cur) {
|
||||
field = GPOINTER_TO_INT(cur->data);
|
||||
if (abs(field) == sort) {
|
||||
cur->data = GINT_TO_POINTER(-field);
|
||||
goto out_sort;
|
||||
}
|
||||
cur = g_slist_next(cur);
|
||||
}
|
||||
|
||||
queue->q_sort = g_slist_append(queue->q_sort, GINT_TO_POINTER(sort));
|
||||
|
||||
out_sort:
|
||||
queue_resort(queue);
|
||||
__queue_save(queue, Q_SAVE_SORT);
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
#include <core/settings.h>
|
||||
|
||||
static GHashTable *gui_settings = NULL;
|
||||
static struct file gui_settings_file = FILE_INIT("settings", 0);
|
||||
static struct file gui_settings_file = FILE_INIT_DATA("", "settings", 0);
|
||||
|
||||
|
||||
static void __settings_save_item(gpointer key, gpointer value, gpointer data)
|
||||
|
@ -29,9 +29,11 @@ static void __settings_read()
|
|||
unsigned int num, i, value;
|
||||
gchar *key;
|
||||
|
||||
file_readf(&gui_settings_file, "%u\n", &num);
|
||||
num = file_readu(&gui_settings_file);
|
||||
for (i = 0; i < num; i++) {
|
||||
file_readf(&gui_settings_file, "%m[^ ] %u\n", &key, &value);
|
||||
key = file_readw(&gui_settings_file);
|
||||
value = file_readu(&gui_settings_file);
|
||||
|
||||
g_hash_table_insert(gui_settings, key, GUINT_TO_POINTER(value));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,3 +81,25 @@ bool string_match_token(const gchar *prefix, gchar **tokens)
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool string_is_subdir(const gchar *a, const gchar *b)
|
||||
{
|
||||
gchar **parent = b ? g_strsplit(b, "/", -1) : NULL;
|
||||
gchar **child = a ? g_strsplit(a, "/", -1) : NULL;
|
||||
bool subdir = true;
|
||||
int i;
|
||||
|
||||
if (!parent || !child)
|
||||
return false;
|
||||
|
||||
for (i = 0; parent[i]; i++) {
|
||||
if (!child[i] || g_utf8_collate(parent[i], child[i]) != 0) {
|
||||
subdir = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_strfreev(parent);
|
||||
g_strfreev(child);
|
||||
return subdir;
|
||||
}
|
||||
|
|
|
@ -19,24 +19,35 @@
|
|||
static struct database album_db;
|
||||
static bool album_db_upgraded = false;
|
||||
|
||||
struct album_cache_file {
|
||||
struct file ac_file;
|
||||
gchar *ac_subdir;
|
||||
gchar *ac_name;
|
||||
};
|
||||
|
||||
static inline void __album_init_file(struct album *al, struct cache_file *f)
|
||||
struct album_cache_file *__album_alloc_file(struct album *al)
|
||||
{
|
||||
struct album_cache_file *ret = g_malloc(sizeof(struct album_cache_file));
|
||||
gchar *name = g_uri_escape_string(al->al_name, " ", true);
|
||||
cache_file_init(f, g_strdup_printf("%d", al->al_year),
|
||||
g_strdup_printf("%s.jpg", name));
|
||||
|
||||
ret->ac_subdir = g_strdup_printf("%d", al->al_year);
|
||||
ret->ac_name = g_strdup_printf("%s.jpg", name);
|
||||
file_init_cache(&ret->ac_file, ret->ac_subdir, ret->ac_name);
|
||||
|
||||
g_free(name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void __album_deinit_file(struct cache_file *f)
|
||||
static inline void __album_free_file(struct album_cache_file *acf)
|
||||
{
|
||||
g_free(f->cf_subdir);
|
||||
g_free(f->cf_name);
|
||||
g_free(acf->ac_subdir);
|
||||
g_free(acf->ac_name);
|
||||
g_free(acf);
|
||||
}
|
||||
|
||||
static bool __album_fetch_cover(struct album *album, gchar *releaseid)
|
||||
{
|
||||
struct cache_file file;
|
||||
struct album_cache_file *file;
|
||||
CaaImageData image;
|
||||
CaaCoverArt *caa;
|
||||
gchar error[256];
|
||||
|
@ -52,13 +63,13 @@ static bool __album_fetch_cover(struct album *album, gchar *releaseid)
|
|||
goto out;
|
||||
}
|
||||
|
||||
__album_init_file(album, &file);
|
||||
if (cache_file_open(&file, OPEN_WRITE)) {
|
||||
cache_file_write(&file, caa_imagedata_data(image),
|
||||
caa_imagedata_size(image));
|
||||
cache_file_close(&file);
|
||||
file = __album_alloc_file(album);
|
||||
if (file_open(&file->ac_file, OPEN_WRITE_BINARY)) {
|
||||
file_write(&file->ac_file, caa_imagedata_data(image),
|
||||
caa_imagedata_size(image));
|
||||
file_close(&file->ac_file);
|
||||
}
|
||||
__album_deinit_file(&file);
|
||||
__album_free_file(file);
|
||||
|
||||
caa_imagedata_delete(image);
|
||||
out:
|
||||
|
@ -127,7 +138,8 @@ static bool __album_query_artist(struct album *album, struct artist *al_artist,
|
|||
gchar *release, *artist, *year;
|
||||
bool found = false;
|
||||
|
||||
if (!al_artist || strcmp(al_artist->ar_tokens[0], "various") == 0)
|
||||
if (!al_artist || !string_length(al_artist->ar_name) ||
|
||||
strcmp(al_artist->ar_tokens[0], "various") == 0)
|
||||
return false;
|
||||
|
||||
release = g_strdup_printf("release:\"%s\"~", lower);
|
||||
|
@ -203,7 +215,7 @@ static struct db_entry *__album_alloc_v0(const gchar *key)
|
|||
return &__album_alloc(NULL, NULL, name, year)->al_dbe;
|
||||
}
|
||||
|
||||
static struct db_entry *album_alloc(const gchar *key)
|
||||
static struct db_entry *album_alloc(const gchar *key, unsigned int index)
|
||||
{
|
||||
unsigned int artist_id, genre_id, year, n;
|
||||
gchar *name;
|
||||
|
@ -241,7 +253,7 @@ static struct album *__album_parse_v0(gchar *line)
|
|||
return __album_alloc(NULL, NULL, name, year);
|
||||
}
|
||||
|
||||
static struct db_entry *album_read(struct file *file)
|
||||
static struct db_entry *album_read(struct file *file, unsigned int index)
|
||||
{
|
||||
unsigned int year, artist_id, genre_id, n;
|
||||
struct album *album;
|
||||
|
@ -351,40 +363,40 @@ bool album_match_token(struct album *album, const gchar *string)
|
|||
|
||||
bool album_artwork_exists(struct album *album)
|
||||
{
|
||||
struct cache_file file;
|
||||
struct album_cache_file *file;
|
||||
bool ret;
|
||||
|
||||
__album_init_file(album, &file);
|
||||
ret = cache_file_exists(&file);
|
||||
__album_deinit_file(&file);
|
||||
file = __album_alloc_file(album);
|
||||
ret = file_exists(&file->ac_file);
|
||||
__album_free_file(file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
gchar *album_artwork_path(struct album *album)
|
||||
{
|
||||
struct cache_file file;
|
||||
struct album_cache_file *file;
|
||||
gchar *ret = NULL;
|
||||
|
||||
__album_init_file(album, &file);
|
||||
if (cache_file_exists(&file))
|
||||
ret = cache_file_path(&file);
|
||||
__album_deinit_file(&file);
|
||||
file = __album_alloc_file(album);
|
||||
if (file_exists(&file->ac_file))
|
||||
ret = file_path(&file->ac_file);
|
||||
__album_free_file(file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool album_artwork_import(struct album *album, gchar *path)
|
||||
{
|
||||
struct cache_file file;
|
||||
struct album_cache_file *file;
|
||||
bool ret = false;
|
||||
|
||||
__album_init_file(album, &file);
|
||||
if (path && cache_file_open(&file, OPEN_WRITE)) {
|
||||
ret = cache_file_import(&file, path);
|
||||
cache_file_close(&file);
|
||||
file = __album_alloc_file(album);
|
||||
if (path && file_open(&file->ac_file, OPEN_WRITE_BINARY)) {
|
||||
ret = file_import(&file->ac_file, path);
|
||||
file_close(&file->ac_file);
|
||||
}
|
||||
__album_deinit_file(&file);
|
||||
__album_free_file(file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ static struct artist *__artist_alloc(gchar *name)
|
|||
}
|
||||
|
||||
|
||||
static struct db_entry *artist_alloc(const gchar *name)
|
||||
static struct db_entry *artist_alloc(const gchar *name, unsigned int index)
|
||||
{
|
||||
return &__artist_alloc(g_strdup(name))->ar_dbe;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ static gchar *artist_key(struct db_entry *dbe)
|
|||
return ARTIST(dbe)->ar_name;
|
||||
}
|
||||
|
||||
struct db_entry *artist_read(struct file *file)
|
||||
struct db_entry *artist_read(struct file *file, unsigned int index)
|
||||
{
|
||||
return &__artist_alloc(file_readl(file))->ar_dbe;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ static struct genre *__genre_alloc(gchar *name)
|
|||
return genre;
|
||||
}
|
||||
|
||||
static struct db_entry *genre_alloc(const gchar *name)
|
||||
static struct db_entry *genre_alloc(const gchar *name, unsigned int index)
|
||||
{
|
||||
return &__genre_alloc(g_strdup(name))->ge_dbe;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ static gchar *genre_key(struct db_entry *dbe)
|
|||
return GENRE(dbe)->ge_name;
|
||||
}
|
||||
|
||||
static struct db_entry *genre_read(struct file *file)
|
||||
static struct db_entry *genre_read(struct file *file, unsigned int index)
|
||||
{
|
||||
return &__genre_alloc(file_readl(file))->ge_dbe;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/string.h>
|
||||
#include <core/tags/library.h>
|
||||
|
||||
#define LIBRARY_DB_MIN 0 /* Ocarina 6.0 */
|
||||
|
@ -17,7 +18,7 @@ static struct library *__library_alloc(gchar *path)
|
|||
return library;
|
||||
}
|
||||
|
||||
static struct db_entry *library_alloc(const gchar *path)
|
||||
static struct db_entry *library_alloc(const gchar *path, unsigned int index)
|
||||
{
|
||||
return &__library_alloc(g_strdup(path))->li_dbe;
|
||||
}
|
||||
|
@ -32,15 +33,15 @@ static gchar *library_key(struct db_entry *dbe)
|
|||
return LIBRARY(dbe)->li_path;
|
||||
}
|
||||
|
||||
static struct db_entry *library_read(struct file *file)
|
||||
static struct db_entry *library_read(struct file *file, unsigned int index)
|
||||
{
|
||||
int enabled;
|
||||
gchar *path;
|
||||
|
||||
/* Old "enabled" flag */
|
||||
if (file_version(file) == 0)
|
||||
file_readf(file, "%d", &enabled);
|
||||
file_readd(file);
|
||||
|
||||
file_readf(file, " %m[^\n]", &path);
|
||||
path = file_readl(file);
|
||||
return &__library_alloc(path)->li_dbe;
|
||||
}
|
||||
|
||||
|
@ -82,12 +83,22 @@ const struct database *library_db_get()
|
|||
|
||||
struct library *library_find(const gchar *path)
|
||||
{
|
||||
return LIBRARY(db_find(&library_db, path));
|
||||
struct library *library = library_lookup(path);
|
||||
if (library)
|
||||
return library;
|
||||
return LIBRARY(db_insert(&library_db, path));
|
||||
}
|
||||
|
||||
struct library *library_lookup(const gchar *path)
|
||||
{
|
||||
return LIBRARY(db_get(&library_db, path));
|
||||
struct db_entry *dbe, *next;
|
||||
|
||||
db_for_each(dbe, next, &library_db) {
|
||||
if (string_is_subdir(path, LIBRARY(dbe)->li_path))
|
||||
return LIBRARY(dbe);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct library *library_get(const unsigned int index)
|
||||
|
|
|
@ -41,26 +41,18 @@ static struct track *__track_alloc()
|
|||
return track;
|
||||
}
|
||||
|
||||
|
||||
struct db_entry *track_alloc(const gchar *key)
|
||||
static struct track *__track_alloc_filepath(const gchar *filepath)
|
||||
{
|
||||
TagLib_File *file = taglib_file_new(filepath);
|
||||
const TagLib_AudioProperties *audio;
|
||||
struct library *library;
|
||||
struct track *track = NULL;
|
||||
struct artist *artist;
|
||||
struct genre *genre;
|
||||
struct track *track = NULL;
|
||||
unsigned int lib_id;
|
||||
TagLib_File *file;
|
||||
TagLib_Tag *tag;
|
||||
char *fullpath, *path;
|
||||
TagLib_Tag *tag;
|
||||
|
||||
sscanf(key, "%u/%m[^\n]", &lib_id, &path);
|
||||
library = library_get(lib_id);
|
||||
fullpath = library_file(library, path);
|
||||
file = taglib_file_new(fullpath);
|
||||
if (!file || !taglib_file_is_valid(file)) {
|
||||
g_printerr("WARNING: Could not read tags for: %s\n", fullpath);
|
||||
goto out;
|
||||
g_printerr("WARNING: Could not read tags for: %s\n", filepath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
track = __track_alloc();
|
||||
|
@ -69,24 +61,50 @@ struct db_entry *track_alloc(const gchar *key)
|
|||
artist = artist_find(taglib_tag_artist(tag));
|
||||
genre = genre_find(taglib_tag_genre(tag));
|
||||
|
||||
track->tr_album = album_find(artist, genre, taglib_tag_album(tag),
|
||||
taglib_tag_year(tag));
|
||||
track->tr_library = library;
|
||||
track->tr_album = album_find(artist, genre, taglib_tag_album(tag),
|
||||
taglib_tag_year(tag));
|
||||
|
||||
unplayed_count++;
|
||||
track->tr_count = 0;
|
||||
track->tr_length = taglib_audioproperties_length(audio);
|
||||
track->tr_track = taglib_tag_track(tag);
|
||||
date_set(&track->tr_date, 0, 0, 0);
|
||||
|
||||
track->tr_path = g_strdup(key);
|
||||
track->tr_title = g_strdup(taglib_tag_title(tag));
|
||||
track->tr_tokens = g_str_tokenize_and_fold(track->tr_title, NULL,
|
||||
&track->tr_alts);
|
||||
|
||||
taglib_tag_free_strings();
|
||||
taglib_file_free(file);
|
||||
out:
|
||||
return track;
|
||||
}
|
||||
|
||||
static void __track_free(struct track *track)
|
||||
{
|
||||
g_strfreev(track->tr_tokens);
|
||||
g_strfreev(track->tr_alts);
|
||||
g_free(track->tr_title);
|
||||
g_free(track);
|
||||
}
|
||||
|
||||
|
||||
struct db_entry *track_alloc(const gchar *key, unsigned int index)
|
||||
{
|
||||
struct library *library;
|
||||
char *fullpath, *path;
|
||||
struct track *track;
|
||||
unsigned int lib_id;
|
||||
|
||||
sscanf(key, "%u/%m[^\n]", &lib_id, &path);
|
||||
library = library_get(lib_id);
|
||||
fullpath = library_file(library, path);
|
||||
track = __track_alloc_filepath(fullpath);
|
||||
|
||||
if (track) {
|
||||
track->tr_library = library;
|
||||
track->tr_path = g_strdup(key);
|
||||
unplayed_count++;
|
||||
}
|
||||
|
||||
g_free(path);
|
||||
g_free(fullpath);
|
||||
return track ? &track->tr_dbe : NULL;
|
||||
|
@ -100,10 +118,7 @@ static void track_free(struct db_entry *dbe)
|
|||
if (track->tr_count == 0)
|
||||
unplayed_count--;
|
||||
|
||||
g_strfreev(track->tr_tokens);
|
||||
g_strfreev(track->tr_alts);
|
||||
g_free(track->tr_title);
|
||||
g_free(track);
|
||||
__track_free(track);
|
||||
}
|
||||
|
||||
static gchar *track_key(struct db_entry *dbe)
|
||||
|
@ -113,42 +128,35 @@ static gchar *track_key(struct db_entry *dbe)
|
|||
|
||||
static void track_read_v0(struct file *file, struct track *track)
|
||||
{
|
||||
unsigned int artist_id, album_id, genre_id;
|
||||
struct artist *artist;
|
||||
struct genre *genre;
|
||||
struct album *album;
|
||||
struct artist *artist = artist_get(file_readu(file));
|
||||
struct album *album = album_get( file_readu(file));
|
||||
struct genre *genre = genre_get( file_readu(file));
|
||||
|
||||
file_readf(file, "%u %u %u %hu", &artist_id, &album_id, &genre_id,
|
||||
&track->tr_track);
|
||||
track->tr_track = file_readhu(file);
|
||||
date_read(file, &track->tr_date);
|
||||
|
||||
album = album_get(album_id);
|
||||
artist = artist_get(artist_id);
|
||||
genre = genre_get(genre_id);
|
||||
|
||||
if (album->al_artist != artist || album->al_genre != genre)
|
||||
album = album_find(artist, genre, album->al_name, album->al_year);
|
||||
|
||||
track->tr_album = album;
|
||||
}
|
||||
|
||||
static struct db_entry *track_read(struct file *file)
|
||||
static struct db_entry *track_read(struct file *file, unsigned int index)
|
||||
{
|
||||
struct track *track = __track_alloc();
|
||||
unsigned int library_id, album_id;
|
||||
|
||||
file_readf(file, "%u", &library_id);
|
||||
track->tr_library = library_get(library_id);
|
||||
track->tr_library = library_get(file_readu(file));
|
||||
|
||||
if (file_version(file) == 0)
|
||||
track_read_v0(file, track);
|
||||
else {
|
||||
file_readf(file, "%u %hu", &album_id, &track->tr_track);
|
||||
track->tr_album = album_get(album_id);
|
||||
track->tr_album = album_get(file_readu(file));
|
||||
track->tr_track = file_readhu(file);
|
||||
date_read_stamp(file, &track->tr_date);
|
||||
}
|
||||
|
||||
file_readf(file, "%hu %hu", &track->tr_count, &track->tr_length);
|
||||
track->tr_count = file_readhu(file);
|
||||
track->tr_length = file_readhu(file);
|
||||
|
||||
play_count += track->tr_count;
|
||||
if (track->tr_count == 0)
|
||||
|
@ -251,6 +259,24 @@ unsigned int track_db_average_plays()
|
|||
return play_count / (track_db.db_size - unplayed_count);
|
||||
}
|
||||
|
||||
struct track *track_alloc_external(const gchar *filepath)
|
||||
{
|
||||
struct track *track = __track_alloc_filepath(filepath);
|
||||
if (track) {
|
||||
track->tr_library = NULL;
|
||||
track->tr_path = g_strdup(filepath);
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
void track_free_external(struct track *track)
|
||||
{
|
||||
if (TRACK_IS_EXTERNAL(track)) {
|
||||
g_free(track->tr_path);
|
||||
__track_free(track);
|
||||
}
|
||||
}
|
||||
|
||||
struct track *track_add(struct library *library, const gchar *filepath)
|
||||
{
|
||||
unsigned int offset = strlen(library->li_path) + 1;
|
||||
|
@ -285,6 +311,23 @@ struct track *track_get(const unsigned int index)
|
|||
return TRACK(db_at(&track_db, index));
|
||||
}
|
||||
|
||||
struct track *track_lookup(const gchar *filepath)
|
||||
{
|
||||
struct library *library = library_lookup(filepath);
|
||||
unsigned int offset;
|
||||
struct track *track;
|
||||
gchar *key;
|
||||
|
||||
if (!library)
|
||||
return NULL;
|
||||
|
||||
offset = strlen(library->li_path) + 1;
|
||||
key = __track_key(library, g_strdup(filepath + offset));
|
||||
track = TRACK(db_get(&track_db, key));
|
||||
g_free(key);
|
||||
return track;
|
||||
}
|
||||
|
||||
int track_compare(struct track *lhs, struct track *rhs, enum compare_t compare)
|
||||
{
|
||||
switch (compare) {
|
||||
|
@ -329,11 +372,13 @@ gchar *track_path(struct track *track)
|
|||
g_free(path);
|
||||
return res;
|
||||
}
|
||||
return g_strdup("");
|
||||
return g_strdup(track->tr_path);
|
||||
}
|
||||
|
||||
void track_played(struct track *track)
|
||||
{
|
||||
if (TRACK_IS_EXTERNAL(track))
|
||||
return;
|
||||
if (track->tr_count == 0)
|
||||
unplayed_count--;
|
||||
track->tr_count++;
|
||||
|
|
162
gui/artwork.c
162
gui/artwork.c
|
@ -2,28 +2,25 @@
|
|||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/audio.h>
|
||||
#include <gui/builder.h>
|
||||
#include <gui/artwork.h>
|
||||
#include <gui/window.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#define ARTWORK_PREVIEW_SIZE 150
|
||||
|
||||
static struct album *__artwork_cur_album = NULL;
|
||||
static unsigned int __artwork_timeo_id = 0;
|
||||
static struct album *artwork_current = NULL;
|
||||
static unsigned int artwork_timeout = 0;
|
||||
|
||||
static cairo_surface_t *__artwork_scale_pixbuf(GdkPixbuf *pix)
|
||||
static cairo_surface_t *__gui_artwork_scale(cairo_surface_t *orig, int new_h)
|
||||
{
|
||||
int old_h = gdk_pixbuf_get_height(pix);
|
||||
int old_w = gdk_pixbuf_get_width(pix);
|
||||
int new_h = gui_builder_widget_height("o_position") +
|
||||
gui_builder_widget_height("o_tags");
|
||||
int old_h = cairo_image_surface_get_height(orig);
|
||||
int old_w = cairo_image_surface_get_width(orig);
|
||||
int new_w = (old_w * new_h) / old_h;
|
||||
int scale = gtk_widget_get_scale_factor(gui_builder_widget("o_cover"));
|
||||
cairo_surface_t *orig = gdk_cairo_surface_create_from_pixbuf(pix, 0,
|
||||
NULL);
|
||||
int scale = gtk_widget_get_scale_factor(GTK_WIDGET(gui_artwork()));
|
||||
cairo_content_t content = cairo_surface_get_content(orig);
|
||||
cairo_surface_t *new = cairo_surface_create_similar(orig, content,
|
||||
cairo_surface_t *scaled = cairo_surface_create_similar(orig, content,
|
||||
new_w, new_h);
|
||||
cairo_t *cairo = cairo_create(new);
|
||||
cairo_t *cairo = cairo_create(scaled);
|
||||
|
||||
cairo_scale(cairo, (double)(scale * new_w) / old_w,
|
||||
(double)(scale * new_h) / old_h);
|
||||
|
@ -31,92 +28,77 @@ static cairo_surface_t *__artwork_scale_pixbuf(GdkPixbuf *pix)
|
|||
cairo_paint(cairo);
|
||||
|
||||
cairo_destroy(cairo);
|
||||
cairo_surface_destroy(orig);
|
||||
return new;
|
||||
return scaled;
|
||||
}
|
||||
|
||||
static cairo_surface_t *__artwork_get_surface(struct track *track)
|
||||
static cairo_surface_t *__gui_artwork_get(gchar *path, int new_h)
|
||||
{
|
||||
gchar *path = album_artwork_path(track->tr_album);
|
||||
cairo_surface_t *surface;
|
||||
GdkPixbuf *pix;
|
||||
GdkPixbuf *pixbuf = path ? gdk_pixbuf_new_from_file(path, NULL) : NULL;
|
||||
cairo_surface_t *surface = NULL;
|
||||
cairo_surface_t *scaled = NULL;
|
||||
|
||||
if (!path)
|
||||
if (!pixbuf)
|
||||
return NULL;
|
||||
|
||||
pix = gdk_pixbuf_new_from_file(path, NULL);
|
||||
if (!pix)
|
||||
return NULL;
|
||||
surface = __artwork_scale_pixbuf(pix);
|
||||
|
||||
g_object_unref(G_OBJECT(pix));
|
||||
g_free(path);
|
||||
return surface;
|
||||
}
|
||||
|
||||
static bool __artwork_set_pixbuf(void)
|
||||
{
|
||||
GtkImage *cover = GTK_IMAGE(gui_builder_widget("o_cover"));
|
||||
struct track *track = audio_cur_track();
|
||||
cairo_surface_t *surface;
|
||||
|
||||
if (!track || track->tr_album == __artwork_cur_album)
|
||||
return true;
|
||||
|
||||
surface = __artwork_get_surface(track);
|
||||
surface = gdk_cairo_surface_create_from_pixbuf(pixbuf, 0, NULL);
|
||||
if (surface) {
|
||||
gtk_image_set_from_surface(cover, surface);
|
||||
scaled = __gui_artwork_scale(surface, new_h);
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
g_object_unref(G_OBJECT(pixbuf));
|
||||
return scaled;
|
||||
}
|
||||
|
||||
static bool __gui_artwork_set_path(GtkImage *image, gchar *path, int new_h)
|
||||
{
|
||||
cairo_surface_t *surface = __gui_artwork_get(path, new_h);
|
||||
bool status = surface != NULL;
|
||||
|
||||
if (surface) {
|
||||
gtk_image_set_from_surface(image, surface);
|
||||
cairo_surface_destroy(surface);
|
||||
} else
|
||||
gtk_image_set_from_icon_name(cover, "image-missing", GTK_ICON_SIZE_DIALOG);
|
||||
gtk_image_set_from_icon_name(image, "image-missing",
|
||||
GTK_ICON_SIZE_DIALOG);
|
||||
|
||||
gtk_widget_set_sensitive(GTK_WIDGET(cover), surface != NULL);
|
||||
__artwork_cur_album = surface ? track->tr_album : NULL;
|
||||
return (surface != NULL);
|
||||
return status;
|
||||
}
|
||||
|
||||
static gboolean __artwork_timeout(gpointer data)
|
||||
static gboolean __gui_artwork_timeout(gpointer data)
|
||||
{
|
||||
if (__artwork_set_pixbuf()) {
|
||||
__artwork_timeo_id = 0;
|
||||
struct track *track = audio_cur_track();
|
||||
int height = gui_builder_widget_height("artwork");
|
||||
gchar *path;
|
||||
bool status;
|
||||
|
||||
if (!track || track->tr_album == artwork_current)
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
return G_SOURCE_CONTINUE;
|
||||
|
||||
path = album_artwork_path(track->tr_album);
|
||||
status = __gui_artwork_set_path(gui_artwork(), path, height);
|
||||
gtk_widget_set_sensitive(GTK_WIDGET(gui_artwork()), status);
|
||||
artwork_current = status ? track->tr_album : NULL;
|
||||
artwork_timeout = status ? artwork_timeout : 0;
|
||||
|
||||
g_free(path);
|
||||
return status ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
void gui_artwork_set_cover(void)
|
||||
{
|
||||
if (!__artwork_set_pixbuf() && __artwork_timeo_id == 0)
|
||||
__artwork_timeo_id = g_timeout_add(2000, __artwork_timeout, NULL);
|
||||
}
|
||||
|
||||
void __artwork_update_preview(GtkFileChooser *chooser, gpointer data)
|
||||
void __gui_artwork_update_preview(GtkFileChooser *chooser, gpointer data)
|
||||
{
|
||||
GtkWidget *preview = gtk_file_chooser_get_preview_widget(chooser);
|
||||
gchar *path = gtk_file_chooser_get_preview_filename(chooser);
|
||||
GdkPixbuf *pix = gdk_pixbuf_new_from_file(path, NULL);
|
||||
unsigned int old_w, scale;
|
||||
cairo_surface_t *surface;
|
||||
|
||||
if (pix) {
|
||||
old_w = gdk_pixbuf_get_width(pix);
|
||||
scale = old_w / ARTWORK_PREVIEW_SIZE;
|
||||
surface = gdk_cairo_surface_create_from_pixbuf(pix, scale, NULL);
|
||||
if (surface) {
|
||||
gtk_image_set_from_surface(GTK_IMAGE(preview), surface);
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
}
|
||||
|
||||
__gui_artwork_set_path(GTK_IMAGE(preview), path, ARTWORK_PREVIEW_SIZE);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
void __artwork_select_cover(GtkButton *button)
|
||||
void __gui_artwork_select_cover(GtkButton *button)
|
||||
{
|
||||
struct track *track = audio_cur_track();
|
||||
GtkWidget *preview, *dialog;
|
||||
GtkFileFilter *filter;
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *preview;
|
||||
gchar *path;
|
||||
|
||||
if (!track)
|
||||
|
@ -124,12 +106,11 @@ void __artwork_select_cover(GtkButton *button)
|
|||
|
||||
filter = gtk_file_filter_new();
|
||||
preview = gtk_image_new_from_icon_name("", GTK_ICON_SIZE_DIALOG);
|
||||
dialog = gtk_file_chooser_dialog_new("Choose an image",
|
||||
GTK_WINDOW(gui_builder_widget("o_window")),
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
_("_Open"), GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
dialog = gtk_file_chooser_dialog_new("Choose an image", gui_window(),
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
_("_Open"), GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
|
||||
gtk_file_filter_add_mime_type(filter, "image/*");
|
||||
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
|
||||
|
@ -137,15 +118,30 @@ void __artwork_select_cover(GtkButton *button)
|
|||
gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog), preview);
|
||||
gtk_file_chooser_set_use_preview_label(GTK_FILE_CHOOSER(dialog), false);
|
||||
|
||||
g_signal_connect(dialog, "update-preview", (GCallback)__artwork_update_preview, NULL);
|
||||
g_signal_connect(dialog, "update-preview",
|
||||
(GCallback)__gui_artwork_update_preview, NULL);
|
||||
|
||||
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
|
||||
path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
|
||||
album_artwork_import(track->tr_album, path);
|
||||
__artwork_cur_album = NULL;
|
||||
gui_artwork_set_cover();
|
||||
gui_artwork_import(track, path);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
gtk_widget_destroy(dialog);
|
||||
}
|
||||
|
||||
void gui_artwork_set_cover(void)
|
||||
{
|
||||
if (__gui_artwork_timeout(NULL) != G_SOURCE_CONTINUE)
|
||||
return;
|
||||
artwork_timeout = g_timeout_add(2000, __gui_artwork_timeout, NULL);
|
||||
}
|
||||
|
||||
void gui_artwork_import(struct track *track, gchar *path)
|
||||
{
|
||||
album_artwork_import(track->tr_album, path);
|
||||
if (track == audio_cur_track()) {
|
||||
artwork_current = NULL;
|
||||
gui_artwork_set_cover();
|
||||
}
|
||||
}
|
||||
|
|
243
gui/audio.c
243
gui/audio.c
|
@ -1,136 +1,205 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/audio.h>
|
||||
#include <core/playlist.h>
|
||||
#include <core/string.h>
|
||||
#include <gui/artwork.h>
|
||||
#include <gui/audio.h>
|
||||
#include <gui/builder.h>
|
||||
#include <gui/idle.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/playlist.h>
|
||||
#include <gui/treeview.h>
|
||||
#include <gui/window.h>
|
||||
|
||||
static inline void __audio_set_label(const gchar *label, const gchar *size,
|
||||
const gchar *text)
|
||||
static guint audio_timeout = 0;
|
||||
static guint popover_timeout = 0;
|
||||
|
||||
static inline void __gui_audio_set_label_markup(GtkLabel *label,
|
||||
const gchar *size,
|
||||
const gchar *text)
|
||||
{
|
||||
gchar *markup = g_markup_printf_escaped("<span size='%s'>%s</span>",
|
||||
size, text);
|
||||
gtk_label_set_markup(GTK_LABEL(gui_builder_widget(label)), markup);
|
||||
const gchar *fmt = "<span size='%s'>%s</span>";
|
||||
gchar *markup = g_markup_printf_escaped(fmt, size, text);
|
||||
gtk_label_set_markup(label, markup);
|
||||
g_free(markup);
|
||||
}
|
||||
|
||||
static inline void __audio_set_time_label(const gchar *label, unsigned int time)
|
||||
static void __gui_audio_load(struct track *track)
|
||||
{
|
||||
gchar *str = string_sec2str(time);
|
||||
__audio_set_label(label, "large", str);
|
||||
g_free(str);
|
||||
}
|
||||
gchar *duration = string_sec2str(track->tr_length);
|
||||
|
||||
static void __audio_load(struct track *track)
|
||||
{
|
||||
__audio_set_label("o_title", "xx-large", track->tr_title);
|
||||
__audio_set_label("o_artist", "x-large", track->tr_album->al_artist->ar_name);
|
||||
__audio_set_label("o_album", "x-large", track->tr_album->al_name);
|
||||
__audio_set_time_label("o_duration", track->tr_length);
|
||||
__gui_audio_set_label_markup(gui_title_tag(), "xx-large",
|
||||
track->tr_title);
|
||||
__gui_audio_set_label_markup(gui_album_tag(), "x-large",
|
||||
track->tr_album->al_name);
|
||||
__gui_audio_set_label_markup(gui_artist_tag(), "x-large",
|
||||
track->tr_album->al_artist->ar_name);
|
||||
__gui_audio_set_label_markup(gui_duration(), "large", duration);
|
||||
__gui_audio_set_label_markup(gui_position(), "large", "0:00");
|
||||
gtk_adjustment_set_upper(gui_seek(), track->tr_length);
|
||||
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gui_builder_widget("o_hide")),
|
||||
playlist_has(PL_SYSTEM, "Hidden", track));
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gui_builder_widget("o_favorite")),
|
||||
playlist_has(PL_SYSTEM, "Favorites", track));
|
||||
|
||||
gui_view_scroll();
|
||||
gui_pl_system_track_loaded(track);
|
||||
gui_treeview_scroll();
|
||||
gui_artwork_set_cover();
|
||||
gui_idle_enable();
|
||||
g_free(duration);
|
||||
}
|
||||
|
||||
static void __audio_change_state(GstState state)
|
||||
static void __gui_audio_set_pause_text(int n, GstState state)
|
||||
{
|
||||
if (state == GST_STATE_PLAYING) {
|
||||
gtk_widget_hide(gui_builder_widget("o_play"));
|
||||
gtk_widget_show(gui_builder_widget("o_pause"));
|
||||
} else {
|
||||
gtk_widget_show(gui_builder_widget("o_play"));
|
||||
gtk_widget_hide(gui_builder_widget("o_pause"));
|
||||
bool sensitive = true;
|
||||
gchar *text;
|
||||
|
||||
if (n == -1) {
|
||||
sensitive = false;
|
||||
if (state == GST_STATE_PLAYING)
|
||||
text = g_strdup("Keep playing");
|
||||
else
|
||||
text = g_strdup("Paused");
|
||||
} else if (n == 0)
|
||||
text = g_strdup("Pause after this track");
|
||||
else if (n == 1)
|
||||
text = g_strdup("Pause after next track");
|
||||
else
|
||||
text = g_strdup_printf("Pause after %d tracks", n);
|
||||
|
||||
gtk_widget_set_sensitive(GTK_WIDGET(gui_pause_down()), sensitive);
|
||||
gtk_entry_set_text(gui_pause_entry(), text);
|
||||
g_free(text);
|
||||
}
|
||||
|
||||
static void __gui_audio_change_state(GstState state)
|
||||
{
|
||||
bool playing = (state == GST_STATE_PLAYING);
|
||||
gtk_widget_set_visible(GTK_WIDGET(gui_play_button()), !playing);
|
||||
gtk_widget_set_visible(GTK_WIDGET(gui_pause_button()), playing);
|
||||
__gui_audio_set_pause_text(audio_get_pause_count(), state);
|
||||
}
|
||||
|
||||
static void __gui_audio_config_pause(int n)
|
||||
{
|
||||
__gui_audio_set_pause_text(n, audio_cur_state());
|
||||
}
|
||||
|
||||
|
||||
struct audio_callbacks audio_cb = {
|
||||
.audio_cb_load = __gui_audio_load,
|
||||
.audio_cb_state_change = __gui_audio_change_state,
|
||||
.audio_cb_config_pause = __gui_audio_config_pause,
|
||||
};
|
||||
|
||||
|
||||
void __gui_audio_pause(GtkButton *button, gpointer data)
|
||||
{
|
||||
audio_pause();
|
||||
if (audio_get_pause_count() > -1) {
|
||||
gtk_popover_popup(gui_pause_popover());
|
||||
popover_timeout = g_timeout_add_seconds(10,
|
||||
gui_audio_popover_timeout, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void __audio_config_pause(int n)
|
||||
void __gui_audio_pause_change_text(GtkEntry *entry, gpointer data)
|
||||
{
|
||||
GtkComboBox *combo = GTK_COMBO_BOX(gui_builder_widget("o_pause_after"));
|
||||
gtk_combo_box_set_active(combo, n + 1);
|
||||
const gchar *text = gtk_entry_get_text(entry);
|
||||
int n = audio_get_pause_count();
|
||||
unsigned int i;
|
||||
|
||||
if (g_str_match_string("Keep", text, true))
|
||||
n = -1;
|
||||
else if (g_str_match_string("This", text, true))
|
||||
n = 0;
|
||||
else if (g_str_match_string("Next", text, true))
|
||||
n = 1;
|
||||
else {
|
||||
for (i = 0; text[i] != '\0'; i++) {
|
||||
if (!g_ascii_isdigit(text[i]))
|
||||
continue;
|
||||
if (i > 0 && text[i-1] == '-')
|
||||
i -= 1;
|
||||
n = g_strtod(text + i, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!audio_pause_after(n))
|
||||
__gui_audio_set_pause_text(audio_get_pause_count(), audio_cur_state());
|
||||
}
|
||||
|
||||
void __audio_pause_changed(GtkComboBox *combo, gpointer data)
|
||||
void __gui_audio_pause_inc(GtkButton *button, gpointer data)
|
||||
{
|
||||
int val = gtk_combo_box_get_active(combo) - 1;
|
||||
audio_pause_after(val);
|
||||
audio_pause_after(audio_get_pause_count() + 1);
|
||||
}
|
||||
|
||||
void __audio_seek(GtkRange *range, GtkScrollType type, double value, gpointer data)
|
||||
void __gui_audio_pause_dec(GtkButton *button, gpointer data)
|
||||
{
|
||||
audio_pause_after(audio_get_pause_count() - 1);
|
||||
}
|
||||
|
||||
void __gui_audio_pause_popover_popdown(GtkButton *button, gpointer data)
|
||||
{
|
||||
gtk_popover_popdown(gui_pause_popover());
|
||||
#ifdef CONFIG_TESTING
|
||||
gtk_widget_hide(GTK_WIDGET(gui_pause_popover()));
|
||||
#endif /* CONFIG_TESTING */
|
||||
|
||||
g_source_remove(popover_timeout);
|
||||
popover_timeout = 0;
|
||||
}
|
||||
|
||||
void __gui_audio_pause_popover_clear(GtkButton *button, gpointer data)
|
||||
{
|
||||
audio_pause_after(-1);
|
||||
__gui_audio_pause_popover_popdown(button, data);
|
||||
}
|
||||
|
||||
void __gui_audio_seek(GtkRange *range, GtkScrollType type,
|
||||
double value, gpointer data)
|
||||
{
|
||||
audio_seek(value * GST_SECOND);
|
||||
}
|
||||
|
||||
void __audio_volume_changed(GtkScaleButton *button, gdouble value, gpointer data)
|
||||
void __gui_audio_volume_changed(GtkScaleButton *button, gdouble value,
|
||||
gpointer data)
|
||||
{
|
||||
audio_set_volume((unsigned int)value);
|
||||
}
|
||||
|
||||
void __audio_favorite(GtkToggleButton *toggle, gpointer data)
|
||||
gboolean __gui_audio_can_accel(GtkWidget *widget, guint signal_id)
|
||||
{
|
||||
if (gtk_toggle_button_get_active(toggle))
|
||||
playlist_add(PL_SYSTEM, "Favorites", audio_cur_track());
|
||||
else
|
||||
playlist_remove(PL_SYSTEM, "Favorites", audio_cur_track());
|
||||
}
|
||||
|
||||
void __audio_hide(GtkToggleButton *toggle, gpointer data)
|
||||
{
|
||||
if (gtk_toggle_button_get_active(toggle)) {
|
||||
if (playlist_add(PL_SYSTEM, "Hidden", audio_cur_track()))
|
||||
audio_next();
|
||||
} else
|
||||
playlist_remove(PL_SYSTEM, "Hidden", audio_cur_track());
|
||||
}
|
||||
|
||||
static int __audio_timeout(gpointer data)
|
||||
{
|
||||
GtkAdjustment *progress = data;
|
||||
|
||||
gtk_adjustment_set_upper(progress, audio_duration() / GST_SECOND);
|
||||
gtk_adjustment_set_value(progress, audio_position() / GST_SECOND);
|
||||
__audio_set_time_label("o_position", audio_position() / GST_SECOND);
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
gboolean __audio_can_accel(GtkWidget *widget, guint signal_id)
|
||||
{
|
||||
GtkWindow *window = GTK_WINDOW(gui_builder_widget("o_window"));
|
||||
g_signal_stop_emission_by_name(widget, "can-activate-accel");
|
||||
return !GTK_IS_ENTRY(gtk_window_get_focus(window)) &&
|
||||
return !GTK_IS_ENTRY(gtk_window_get_focus(gui_window())) &&
|
||||
gtk_widget_is_visible(widget) &&
|
||||
gtk_widget_is_sensitive(widget);
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct audio_ops audio_ops = {
|
||||
__audio_load,
|
||||
__audio_change_state,
|
||||
__audio_config_pause,
|
||||
};
|
||||
|
||||
void gui_audio_init()
|
||||
{
|
||||
GtkScaleButton *volume = GTK_SCALE_BUTTON(gui_builder_widget("o_volume"));
|
||||
g_timeout_add(500, __audio_timeout, gui_builder_object("o_progress"));
|
||||
gtk_scale_button_set_value(volume, audio_get_volume());
|
||||
gtk_button_set_relief(GTK_BUTTON(volume), GTK_RELIEF_NORMAL);
|
||||
gtk_scale_button_set_value(gui_volume_button(), audio_get_volume());
|
||||
gtk_button_set_relief(GTK_BUTTON(gui_volume_button()), GTK_RELIEF_NORMAL);
|
||||
|
||||
audio_timeout = g_timeout_add(500, gui_audio_timeout, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TESTING
|
||||
void test_gui_audio_timeout()
|
||||
void gui_audio_deinit()
|
||||
{
|
||||
__audio_timeout(gui_builder_object("o_progress"));
|
||||
g_source_remove(audio_timeout);
|
||||
if (popover_timeout > 0)
|
||||
g_source_remove(popover_timeout);
|
||||
}
|
||||
|
||||
int gui_audio_timeout(gpointer data)
|
||||
{
|
||||
gchar *position = string_sec2str(audio_position() / GST_SECOND);
|
||||
|
||||
gtk_adjustment_set_upper(gui_seek(), audio_duration() / GST_SECOND);
|
||||
gtk_adjustment_set_value(gui_seek(), audio_position() / GST_SECOND);
|
||||
__gui_audio_set_label_markup(gui_position(), "large", position);
|
||||
|
||||
g_free(position);
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
int gui_audio_popover_timeout(gpointer data)
|
||||
{
|
||||
__gui_audio_pause_popover_popdown(NULL, data);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
#endif /* CONFIG_TESTING */
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/idle.h>
|
||||
#include <core/playlist.h>
|
||||
#include <gui/builder.h>
|
||||
#include <gui/idle.h>
|
||||
#include <gui/playlist.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
static void __collection_add(GtkFileChooser *chooser)
|
||||
{
|
||||
gchar *filename = gtk_file_chooser_get_filename(chooser);
|
||||
|
||||
if (playlist_new(PL_LIBRARY, filename))
|
||||
gui_playlist_add_library(library_lookup(filename));
|
||||
|
||||
gui_idle_enable();
|
||||
g_free(filename);
|
||||
}
|
||||
|
||||
void __collection_choose(GtkButton *button, gpointer data)
|
||||
{
|
||||
GtkFileFilter *filter;
|
||||
GtkWidget *dialog;
|
||||
const gchar *path;
|
||||
gint res;
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_add_mime_type(filter, "inode/directory");
|
||||
|
||||
dialog = gtk_file_chooser_dialog_new("Add Music",
|
||||
GTK_WINDOW(gui_builder_widget("o_window")),
|
||||
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
_("_Open"), GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
|
||||
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
|
||||
|
||||
path = g_get_user_special_dir(G_USER_DIRECTORY_MUSIC);
|
||||
if (path)
|
||||
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
|
||||
path);
|
||||
|
||||
res = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
if (res == GTK_RESPONSE_ACCEPT)
|
||||
__collection_add(GTK_FILE_CHOOSER(dialog));
|
||||
|
||||
gtk_widget_destroy(dialog);
|
||||
}
|
||||
|
||||
bool __gui_collection_init_idle()
|
||||
{
|
||||
struct db_entry *library, *next;
|
||||
|
||||
db_for_each(library, next, library_db_get())
|
||||
gui_playlist_add_library(LIBRARY(library));
|
||||
|
||||
if (library_db_get()->db_size == 0)
|
||||
__collection_choose(NULL, NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void gui_collection_init()
|
||||
{
|
||||
idle_schedule(IDLE_SYNC, __gui_collection_init_idle, NULL);
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/audio.h>
|
||||
#include <core/string.h>
|
||||
#include <gui/filter.h>
|
||||
#include <gui/model.h>
|
||||
|
||||
static GtkTreeModelFilter *filter_model = NULL;
|
||||
|
||||
static inline GtkTreePath *__gui_filter_convert_path(GtkTreePath *path)
|
||||
{
|
||||
return gtk_tree_model_filter_convert_path_to_child_path(filter_model,
|
||||
path);
|
||||
}
|
||||
|
||||
static inline gboolean __gui_filter_match_token(struct track *track,
|
||||
const gchar *token,
|
||||
unsigned int how)
|
||||
{
|
||||
switch (how) {
|
||||
case GUI_FILTER_ALBUM:
|
||||
return album_match_token(track->tr_album, token);
|
||||
case GUI_FILTER_ARTIST:
|
||||
return artist_match_token(track->tr_album->al_artist, token);
|
||||
case GUI_FILTER_GENRE:
|
||||
return genre_match_token(track->tr_album->al_genre, token);
|
||||
case GUI_FILTER_TITLE:
|
||||
return track_match_token(track, token);
|
||||
case GUI_FILTER_DEFAULT:
|
||||
return track_match_token(track, token) ||
|
||||
album_match_token(track->tr_album, token) ||
|
||||
artist_match_token(track->tr_album->al_artist, token) ||
|
||||
genre_match_token(track->tr_album->al_genre, token);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean __gui_filter_visible_func(GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer data)
|
||||
{
|
||||
unsigned int i, how = gtk_combo_box_get_active(gui_filter_how());
|
||||
gchar **search = gui_model_get_playlist()->pl_search;
|
||||
struct track *track;
|
||||
|
||||
if (!search)
|
||||
return TRUE;
|
||||
|
||||
track = gui_model_iter_get_track(iter);
|
||||
for (i = 0; search[i]; i++) {
|
||||
if (!__gui_filter_match_token(track, search[i], how))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void __gui_filter_search_changed(GtkSearchEntry *search, gpointer data)
|
||||
{
|
||||
playlist_set_search(gui_model_get_playlist(),
|
||||
gtk_entry_get_text(GTK_ENTRY(search)));
|
||||
gtk_tree_model_filter_refilter(gui_filter_get());
|
||||
}
|
||||
|
||||
void __gui_filter_how_changed(int n)
|
||||
{
|
||||
__gui_filter_search_changed(gui_filter_search(), NULL);
|
||||
}
|
||||
|
||||
void gui_filter_init()
|
||||
{
|
||||
GtkTreeModel *model = GTK_TREE_MODEL(gui_model_get());
|
||||
GtkTreeModel *filter = gtk_tree_model_filter_new(model, NULL);
|
||||
filter_model = GTK_TREE_MODEL_FILTER(filter);
|
||||
|
||||
gtk_tree_model_filter_set_visible_func(filter_model,
|
||||
__gui_filter_visible_func,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
void gui_filter_deinit()
|
||||
{
|
||||
g_object_unref(G_OBJECT(filter_model));
|
||||
}
|
||||
|
||||
void gui_filter_set_playlist(struct playlist *playlist)
|
||||
{
|
||||
gchar **search = playlist ? (gchar **)playlist->pl_search : NULL;
|
||||
gchar *text = search ? g_strjoinv(" ", search) : g_strdup("");
|
||||
|
||||
gui_model_set_playlist(playlist);
|
||||
gtk_entry_set_text(GTK_ENTRY(gui_filter_search()), text);
|
||||
g_free(text);
|
||||
}
|
||||
|
||||
GtkTreeModelFilter *gui_filter_get()
|
||||
{
|
||||
return filter_model;
|
||||
}
|
||||
|
||||
struct track *gui_filter_path_get_track(GtkTreePath *path)
|
||||
{
|
||||
GtkTreePath *real = __gui_filter_convert_path(path);
|
||||
struct track *track = real ? gui_model_path_get_track(real) : NULL;
|
||||
gtk_tree_path_free(real);
|
||||
return track;
|
||||
}
|
||||
|
||||
void gui_filter_path_load_track(GtkTreePath *path)
|
||||
{
|
||||
struct track *track = gui_filter_path_get_track(path);
|
||||
unsigned int index = gtk_tree_path_get_indices(path)[0];
|
||||
|
||||
audio_load(track);
|
||||
playlist_current_set(gui_model_get_playlist(), index);
|
||||
}
|
||||
|
||||
unsigned int gui_filter_path_get_index(GtkTreePath *path)
|
||||
{
|
||||
GtkTreePath *real = __gui_filter_convert_path(path);
|
||||
unsigned int ret = gtk_tree_path_get_indices(real)[0];
|
||||
gtk_tree_path_free(real);
|
||||
return ret;
|
||||
}
|
||||
|
||||
GtkTreePath *gui_filter_path_from_index(unsigned int index)
|
||||
{
|
||||
GtkTreePath *real, *path;
|
||||
|
||||
real = gtk_tree_path_new_from_indices(index, -1);
|
||||
path = gtk_tree_model_filter_convert_child_path_to_path(filter_model,
|
||||
real);
|
||||
gtk_tree_path_free(real);
|
||||
return path;
|
||||
}
|
||||
|
||||
void gui_filter_refilter(struct playlist *playlist)
|
||||
{
|
||||
if (!playlist || playlist == gui_model_get_playlist())
|
||||
gtk_tree_model_filter_refilter(gui_filter_get());
|
||||
}
|
31
gui/idle.c
31
gui/idle.c
|
@ -1,38 +1,27 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/idle.h>
|
||||
#include <gui/builder.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <gui/idle.h>
|
||||
|
||||
static guint idle_id = 0;
|
||||
|
||||
static gboolean __on_idle(gpointer data)
|
||||
{
|
||||
GtkProgressBar *progress = GTK_PROGRESS_BAR(data);
|
||||
unsigned int percent = idle_progress() * 100;
|
||||
gchar *text;
|
||||
bool more = idle_run_task();
|
||||
gchar *text = g_strdup_printf("%f%%", idle_progress() * 100);
|
||||
|
||||
if (idle_run_task()) {
|
||||
gtk_progress_bar_set_fraction(progress, idle_progress());
|
||||
gtk_widget_set_visible(GTK_WIDGET(gui_progress_bar()), more);
|
||||
gtk_progress_bar_set_fraction(gui_progress_bar(), idle_progress());
|
||||
gtk_widget_set_tooltip_text(GTK_WIDGET(gui_progress_bar()), text);
|
||||
g_free(text);
|
||||
|
||||
text = g_strdup_printf("%u%%", percent);
|
||||
gtk_widget_set_tooltip_text(GTK_WIDGET(progress), text);
|
||||
g_free(text);
|
||||
return G_SOURCE_CONTINUE;
|
||||
} else {
|
||||
gtk_widget_hide(GTK_WIDGET(progress));
|
||||
idle_id = 0;
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
idle_id = more ? idle_id : 0;
|
||||
return more ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
void gui_idle_enable()
|
||||
{
|
||||
GtkWidget *progress = gui_builder_widget("o_idle_progress");
|
||||
|
||||
gtk_widget_show(progress);
|
||||
idle_id = g_idle_add(__on_idle, progress);
|
||||
idle_id = g_idle_add(__on_idle, NULL);
|
||||
}
|
||||
|
||||
void gui_idle_disable()
|
||||
|
|
439
gui/model.c
439
gui/model.c
|
@ -2,66 +2,57 @@
|
|||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/audio.h>
|
||||
#include <core/queue.h>
|
||||
#include <core/string.h>
|
||||
#include <gui/builder.h>
|
||||
#include <gui/model.h>
|
||||
|
||||
static const GTypeInfo queue_type_info;
|
||||
static const GInterfaceInfo queue_tree_model;
|
||||
static GObjectClass *parent_class = NULL;
|
||||
static GuiQueueModel *queue_model = NULL;
|
||||
static gchar *font_current = "bold";
|
||||
static gchar *font_regular = "";
|
||||
static gboolean __gui_model_iter_nth_child(GtkTreeModel *, GtkTreeIter *,
|
||||
GtkTreeIter *, gint);
|
||||
|
||||
static GType queue_columns[Q_MODEL_N_COLUMNS] = {
|
||||
[Q_MODEL_TRACK_NR] = G_TYPE_UINT,
|
||||
[Q_MODEL_TITLE] = G_TYPE_STRING,
|
||||
[Q_MODEL_LENGTH] = G_TYPE_STRING,
|
||||
[Q_MODEL_ARTIST] = G_TYPE_STRING,
|
||||
[Q_MODEL_ALBUM] = G_TYPE_STRING,
|
||||
[Q_MODEL_YEAR] = G_TYPE_UINT,
|
||||
[Q_MODEL_GENRE] = G_TYPE_STRING,
|
||||
[Q_MODEL_COUNT] = G_TYPE_UINT,
|
||||
[Q_MODEL_LAST_PLAY] = G_TYPE_STRING,
|
||||
[Q_MODEL_FILE_PATH] = G_TYPE_STRING,
|
||||
[Q_MODEL_FONT] = G_TYPE_STRING,
|
||||
static struct playlist *cur_playlist = NULL;
|
||||
static GObjectClass *parent_class = NULL;
|
||||
static GuiModel *gui_model = NULL;
|
||||
static GType gui_model_type = 0;
|
||||
|
||||
static GType gui_model_columns[GUI_MODEL_N_COLUMNS] = {
|
||||
[GUI_MODEL_TRACK_NR] = G_TYPE_UINT,
|
||||
[GUI_MODEL_TITLE] = G_TYPE_STRING,
|
||||
[GUI_MODEL_LENGTH] = G_TYPE_STRING,
|
||||
[GUI_MODEL_ARTIST] = G_TYPE_STRING,
|
||||
[GUI_MODEL_ALBUM] = G_TYPE_STRING,
|
||||
[GUI_MODEL_YEAR] = G_TYPE_UINT,
|
||||
[GUI_MODEL_GENRE] = G_TYPE_STRING,
|
||||
[GUI_MODEL_COUNT] = G_TYPE_UINT,
|
||||
[GUI_MODEL_LAST_PLAY] = G_TYPE_STRING,
|
||||
[GUI_MODEL_FILE_PATH] = G_TYPE_STRING,
|
||||
[GUI_MODEL_FONT] = G_TYPE_STRING,
|
||||
};
|
||||
|
||||
static gboolean __queue_model_iter_nth(GuiQueueModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gint n)
|
||||
{
|
||||
if (!model->gqm_queue || n >= queue_size(model->gqm_queue))
|
||||
return FALSE;
|
||||
const GtkTargetEntry gui_model_drag_targets[] = {
|
||||
{ GUI_DRAG_DATA, GTK_TARGET_SAME_APP, 0 },
|
||||
};
|
||||
|
||||
queue_iter_set(model->gqm_queue, &model->gqm_iter, n);
|
||||
iter->stamp = model->gqm_stamp;
|
||||
iter->user_data = &model->gqm_iter;
|
||||
iter->user_data2 = queue_iter_val(&model->gqm_iter);
|
||||
return TRUE;
|
||||
}
|
||||
const unsigned int gui_model_n_targets = G_N_ELEMENTS(gui_model_drag_targets);
|
||||
|
||||
|
||||
static GtkTreeModelFlags _queue_model_get_flags(GtkTreeModel *model)
|
||||
static GtkTreeModelFlags __gui_model_get_flags(GtkTreeModel *model)
|
||||
{
|
||||
return GTK_TREE_MODEL_LIST_ONLY;
|
||||
}
|
||||
|
||||
static gint _queue_model_get_n_columns(GtkTreeModel *model)
|
||||
static gint __gui_model_get_n_columns(GtkTreeModel *model)
|
||||
{
|
||||
return Q_MODEL_N_COLUMNS;
|
||||
return GUI_MODEL_N_COLUMNS;
|
||||
}
|
||||
|
||||
static GType _queue_model_get_column_type(GtkTreeModel *model, gint index)
|
||||
static GType __gui_model_get_column_type(GtkTreeModel *model, gint index)
|
||||
{
|
||||
g_return_val_if_fail(index >= 0 && index < Q_MODEL_N_COLUMNS,
|
||||
G_TYPE_INVALID);
|
||||
return queue_columns[index];
|
||||
g_return_val_if_fail(index >= 0, G_TYPE_INVALID);
|
||||
g_return_val_if_fail(index < GUI_MODEL_N_COLUMNS, G_TYPE_INVALID);
|
||||
return gui_model_columns[index];
|
||||
}
|
||||
|
||||
static gboolean _queue_model_get_iter(GtkTreeModel *model, GtkTreeIter *iter,
|
||||
GtkTreePath *path)
|
||||
static gboolean __gui_model_get_iter(GtkTreeModel *model, GtkTreeIter *iter,
|
||||
GtkTreePath *path)
|
||||
{
|
||||
gint *indices, depth;
|
||||
|
||||
|
@ -69,320 +60,324 @@ static gboolean _queue_model_get_iter(GtkTreeModel *model, GtkTreeIter *iter,
|
|||
indices = gtk_tree_path_get_indices_with_depth(path, &depth);
|
||||
g_assert(depth == 1);
|
||||
|
||||
return __queue_model_iter_nth(GUI_QUEUE_MODEL(model), iter, indices[0]);
|
||||
return __gui_model_iter_nth_child(model, iter, NULL, indices[0]);
|
||||
}
|
||||
|
||||
static GtkTreePath *_queue_model_get_path(GtkTreeModel *model, GtkTreeIter *iter)
|
||||
static GtkTreePath *__gui_model_get_path(GtkTreeModel *model, GtkTreeIter *iter)
|
||||
{
|
||||
struct queue_iter *q_it;
|
||||
GtkTreePath *path;
|
||||
unsigned int pos;
|
||||
|
||||
g_return_val_if_fail(iter != NULL, FALSE);
|
||||
g_return_val_if_fail(iter->user_data, FALSE);
|
||||
g_return_val_if_fail(iter->user_data2, FALSE);
|
||||
q_it = iter->user_data;
|
||||
g_return_val_if_fail(iter != NULL, FALSE);
|
||||
g_return_val_if_fail(iter->user_data, FALSE);
|
||||
|
||||
path = gtk_tree_path_new();
|
||||
gtk_tree_path_append_index(path, q_it->it_pos);
|
||||
pos = playlist_iter_index(cur_playlist, iter->user_data);
|
||||
gtk_tree_path_append_index(path, pos);
|
||||
return path;
|
||||
}
|
||||
|
||||
static void _queue_model_get_value(GtkTreeModel *model, GtkTreeIter *iter,
|
||||
gint column, GValue *value)
|
||||
static void __gui_model_get_value(GtkTreeModel *model, GtkTreeIter *iter,
|
||||
gint column, GValue *value)
|
||||
{
|
||||
struct track *track = iter->user_data2;
|
||||
struct track *track = playlist_iter_track(iter->user_data);
|
||||
gchar *str;
|
||||
|
||||
g_return_if_fail(iter != NULL);
|
||||
g_return_if_fail(iter->user_data2 != NULL);
|
||||
g_return_if_fail(column < Q_MODEL_N_COLUMNS);
|
||||
g_return_if_fail(iter->user_data != NULL);
|
||||
g_return_if_fail(column < GUI_MODEL_N_COLUMNS);
|
||||
|
||||
g_value_init(value, queue_columns[column]);
|
||||
g_value_init(value, gui_model_columns[column]);
|
||||
|
||||
switch (column) {
|
||||
case Q_MODEL_TRACK_NR:
|
||||
case GUI_MODEL_TRACK_NR:
|
||||
g_value_set_uint(value, track->tr_track);
|
||||
break;
|
||||
case Q_MODEL_TITLE:
|
||||
case GUI_MODEL_TITLE:
|
||||
g_value_set_static_string(value, track->tr_title);
|
||||
break;
|
||||
case Q_MODEL_LENGTH:
|
||||
case GUI_MODEL_LENGTH:
|
||||
g_value_take_string(value, string_sec2str(track->tr_length));
|
||||
break;
|
||||
case Q_MODEL_ARTIST:
|
||||
case GUI_MODEL_ARTIST:
|
||||
g_value_set_static_string(value, track->tr_album->al_artist->ar_name);
|
||||
break;
|
||||
case Q_MODEL_ALBUM:
|
||||
case GUI_MODEL_ALBUM:
|
||||
g_value_set_static_string(value, track->tr_album->al_name);
|
||||
break;
|
||||
case Q_MODEL_YEAR:
|
||||
case GUI_MODEL_YEAR:
|
||||
g_value_set_uint(value, track->tr_album->al_year);
|
||||
break;
|
||||
case Q_MODEL_GENRE:
|
||||
case GUI_MODEL_GENRE:
|
||||
g_value_set_static_string(value, track->tr_album->al_genre->ge_name);
|
||||
break;
|
||||
case Q_MODEL_COUNT:
|
||||
case GUI_MODEL_COUNT:
|
||||
g_value_set_uint(value, track->tr_count);
|
||||
break;
|
||||
case Q_MODEL_LAST_PLAY:
|
||||
case GUI_MODEL_LAST_PLAY:
|
||||
g_value_take_string(value, track_last_play(track));
|
||||
break;
|
||||
case Q_MODEL_FILE_PATH:
|
||||
case GUI_MODEL_FILE_PATH:
|
||||
str = track_path(track);
|
||||
g_value_take_string(value, g_markup_escape_text(str, -1));
|
||||
g_free(str);
|
||||
break;
|
||||
case Q_MODEL_FONT:
|
||||
str = (track == audio_cur_track()) ? font_current : font_regular;
|
||||
g_value_set_static_string(value, str);
|
||||
case GUI_MODEL_FONT:
|
||||
str = (track == audio_cur_track()) ? "bold" : "";
|
||||
g_value_take_string(value, g_strdup(str));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean _queue_model_iter_next(GtkTreeModel *model, GtkTreeIter *iter)
|
||||
static gboolean __gui_model_iter_next(GtkTreeModel *model, GtkTreeIter *iter)
|
||||
{
|
||||
GuiQueueModel *gqm = GUI_QUEUE_MODEL(model);
|
||||
g_return_val_if_fail(iter != NULL, FALSE);
|
||||
g_return_val_if_fail(iter->user_data, FALSE);
|
||||
|
||||
g_return_val_if_fail(iter != NULL, FALSE);
|
||||
g_return_val_if_fail(iter->user_data, FALSE);
|
||||
g_return_val_if_fail(iter->user_data2, FALSE);
|
||||
|
||||
queue_iter_next(&gqm->gqm_iter);
|
||||
if (gqm->gqm_iter.it_iter == NULL)
|
||||
return FALSE;
|
||||
|
||||
iter->stamp = gqm->gqm_stamp;
|
||||
iter->user_data = &gqm->gqm_iter;
|
||||
iter->user_data2 = queue_iter_val(&gqm->gqm_iter);
|
||||
return TRUE;
|
||||
iter->user_data = playlist_iter_next(iter->user_data);
|
||||
return iter->user_data != NULL;
|
||||
}
|
||||
|
||||
static gboolean _queue_model_iter_children(GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
GtkTreeIter *parent)
|
||||
static gboolean __gui_model_iter_children(GtkTreeModel *model, GtkTreeIter *iter,
|
||||
GtkTreeIter *parent)
|
||||
{
|
||||
if (parent)
|
||||
return FALSE;
|
||||
return __queue_model_iter_nth(GUI_QUEUE_MODEL(model), iter, 0);
|
||||
return __gui_model_iter_nth_child(model, iter, parent, 0);
|
||||
}
|
||||
|
||||
static gboolean _queue_model_iter_has_child(GtkTreeModel *model,
|
||||
GtkTreeIter *iter)
|
||||
static gboolean __gui_model_iter_has_child(GtkTreeModel *model, GtkTreeIter *iter)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gint _queue_model_iter_n_children(GtkTreeModel *model,
|
||||
GtkTreeIter *iter)
|
||||
static gint __gui_model_iter_n_children(GtkTreeModel *model, GtkTreeIter *iter)
|
||||
{
|
||||
if (iter != NULL || !queue_model->gqm_queue)
|
||||
if (iter != NULL || !cur_playlist)
|
||||
return 0;
|
||||
return queue_size(GUI_QUEUE_MODEL(model)->gqm_queue);
|
||||
return playlist_size(cur_playlist);
|
||||
}
|
||||
|
||||
static gboolean _queue_model_iter_nth_child(GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
GtkTreeIter *parent,
|
||||
gint n)
|
||||
static gboolean __gui_model_iter_nth_child(GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
GtkTreeIter *parent,
|
||||
gint n)
|
||||
{
|
||||
if (parent)
|
||||
if (parent || !cur_playlist || n >= playlist_size(cur_playlist))
|
||||
return FALSE;
|
||||
return __queue_model_iter_nth(GUI_QUEUE_MODEL(model), iter, n);
|
||||
|
||||
iter->stamp = gui_model->gm_stamp;
|
||||
iter->user_data = playlist_iter_get(cur_playlist, n);
|
||||
return iter->user_data != NULL;
|
||||
}
|
||||
|
||||
static gboolean _queue_model_iter_parent(GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
GtkTreeIter *child)
|
||||
static gboolean __gui_model_iter_parent(GtkTreeModel *model, GtkTreeIter *iter,
|
||||
GtkTreeIter *child)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void _queue_model_init(GuiQueueModel *model)
|
||||
static gboolean __gui_model_drag_data_get(GtkTreeDragSource *drag_source,
|
||||
GtkTreePath *path,
|
||||
GtkSelectionData *selection_data)
|
||||
{
|
||||
model->gqm_stamp = g_random_int();
|
||||
struct gui_model_drag_data *data = g_malloc(sizeof(*data));
|
||||
|
||||
data->drag_row = gtk_tree_path_get_indices(path)[0];
|
||||
data->drag_track = gui_model_path_get_track(path);
|
||||
|
||||
gtk_selection_data_set(selection_data, gdk_atom_intern(GUI_DRAG_DATA, false),
|
||||
8 /* bytes */, (void *)data, sizeof(*data));
|
||||
g_free(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _queue_model_finalize(GObject *object)
|
||||
static gboolean __gui_model_drag_data_delete(GtkTreeDragSource *drag_source,
|
||||
GtkTreePath *path)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __gui_model_init(GuiModel *model)
|
||||
{
|
||||
model->gm_stamp = g_random_int();
|
||||
}
|
||||
|
||||
static void __gui_model_finalize(GObject *object)
|
||||
{
|
||||
parent_class->finalize(object);
|
||||
}
|
||||
|
||||
static void _queue_model_class_init(GuiQueueModelClass *class)
|
||||
static void __gui_model_class_init(GuiModelClass *class)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GObjectClass *object_class = (GObjectClass *)class;
|
||||
|
||||
object_class->finalize = __gui_model_finalize;
|
||||
parent_class = g_type_class_peek_parent(class);
|
||||
object_class = (GObjectClass *)class;
|
||||
|
||||
object_class->finalize = _queue_model_finalize;
|
||||
}
|
||||
|
||||
static void _queue_tree_model_init(GtkTreeModelIface *iface)
|
||||
static void __gui_tree_model_init(GtkTreeModelIface *iface)
|
||||
{
|
||||
iface->get_flags = _queue_model_get_flags;
|
||||
iface->get_n_columns = _queue_model_get_n_columns;
|
||||
iface->get_column_type = _queue_model_get_column_type;
|
||||
iface->get_iter = _queue_model_get_iter;
|
||||
iface->get_path = _queue_model_get_path;
|
||||
iface->get_value = _queue_model_get_value;
|
||||
iface->iter_next = _queue_model_iter_next;
|
||||
iface->iter_children = _queue_model_iter_children;
|
||||
iface->iter_has_child = _queue_model_iter_has_child;
|
||||
iface->iter_n_children = _queue_model_iter_n_children;
|
||||
iface->iter_nth_child = _queue_model_iter_nth_child;
|
||||
iface->iter_parent = _queue_model_iter_parent;
|
||||
iface->get_flags = __gui_model_get_flags;
|
||||
iface->get_n_columns = __gui_model_get_n_columns;
|
||||
iface->get_column_type = __gui_model_get_column_type;
|
||||
iface->get_iter = __gui_model_get_iter;
|
||||
iface->get_path = __gui_model_get_path;
|
||||
iface->get_value = __gui_model_get_value;
|
||||
iface->iter_next = __gui_model_iter_next;
|
||||
iface->iter_children = __gui_model_iter_children;
|
||||
iface->iter_has_child = __gui_model_iter_has_child;
|
||||
iface->iter_n_children = __gui_model_iter_n_children;
|
||||
iface->iter_nth_child = __gui_model_iter_nth_child;
|
||||
iface->iter_parent = __gui_model_iter_parent;
|
||||
}
|
||||
|
||||
static void __gui_drag_source_init(GtkTreeDragSourceIface *iface)
|
||||
{
|
||||
iface->drag_data_get = __gui_model_drag_data_get;
|
||||
iface->drag_data_delete = __gui_model_drag_data_delete;
|
||||
}
|
||||
|
||||
static void __gui_queue_model_set_runtime(void)
|
||||
static const GTypeInfo gui_model_type_info = {
|
||||
.class_size = sizeof(GuiModelClass),
|
||||
.base_init = NULL,
|
||||
.base_finalize = NULL,
|
||||
.class_init = (GClassInitFunc)__gui_model_class_init,
|
||||
.class_finalize = NULL,
|
||||
.class_data = NULL,
|
||||
.instance_size = sizeof(GuiModel),
|
||||
.n_preallocs = 0,
|
||||
.instance_init = (GInstanceInitFunc)__gui_model_init,
|
||||
};
|
||||
|
||||
static const GInterfaceInfo gui_tree_model = {
|
||||
.interface_init = (GInterfaceInitFunc)__gui_tree_model_init,
|
||||
.interface_finalize = NULL,
|
||||
.interface_data = NULL,
|
||||
};
|
||||
|
||||
static const GInterfaceInfo gui_drag_source = {
|
||||
.interface_init = (GInterfaceInitFunc)__gui_drag_source_init,
|
||||
.interface_finalize = NULL,
|
||||
.interface_data = NULL,
|
||||
};
|
||||
|
||||
|
||||
void gui_model_init(void)
|
||||
{
|
||||
gui_model_type = g_type_register_static(G_TYPE_OBJECT, "GuiModel",
|
||||
&gui_model_type_info,
|
||||
(GTypeFlags)0);
|
||||
g_type_add_interface_static(gui_model_type, GTK_TYPE_TREE_MODEL,
|
||||
&gui_tree_model);
|
||||
g_type_add_interface_static(gui_model_type, GTK_TYPE_TREE_DRAG_SOURCE,
|
||||
&gui_drag_source);
|
||||
|
||||
|
||||
gui_model = g_object_new(gui_model_type, NULL);
|
||||
g_assert(gui_model != NULL);
|
||||
}
|
||||
|
||||
void gui_model_deinit(void)
|
||||
{
|
||||
g_object_unref(gui_model);
|
||||
gui_model_type = 0;
|
||||
gui_model = NULL;
|
||||
cur_playlist = NULL;
|
||||
}
|
||||
|
||||
static void __gui_model_set_runtime(void)
|
||||
{
|
||||
gchar *len = NULL;
|
||||
|
||||
if (queue_model->gqm_queue)
|
||||
len = string_sec2str_long(queue_model->gqm_queue->q_length);
|
||||
if (cur_playlist)
|
||||
len = string_sec2str_long(cur_playlist->pl_length);
|
||||
|
||||
gtk_label_set_text(GTK_LABEL(gui_builder_widget("o_runtime")), len);
|
||||
gtk_label_set_text(gui_model_runtime(), len);
|
||||
g_free(len);
|
||||
}
|
||||
|
||||
void gui_queue_model_init(void)
|
||||
static gboolean __gui_model_foreach_changed(GtkTreeModel *model, GtkTreePath *path,
|
||||
GtkTreeIter *iter, gpointer data)
|
||||
{
|
||||
queue_model = g_object_new(GUI_QUEUE_MODEL_TYPE, NULL);
|
||||
g_assert(queue_model != NULL);
|
||||
queue_model->gqm_queue = NULL;
|
||||
if (!data || data == gui_model_iter_get_track(iter))
|
||||
gtk_tree_model_row_changed(model, path, iter);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void gui_queue_model_deinit(void)
|
||||
GuiModel *gui_model_get(void)
|
||||
{
|
||||
g_object_unref(queue_model);
|
||||
queue_model = NULL;
|
||||
return gui_model;
|
||||
}
|
||||
|
||||
GuiQueueModel *gui_queue_model_get(void)
|
||||
GType gui_model_get_type()
|
||||
{
|
||||
return queue_model;
|
||||
return gui_model_type;
|
||||
}
|
||||
|
||||
GType gui_queue_model_get_type()
|
||||
{
|
||||
static GType queue_type = 0;
|
||||
|
||||
if (queue_type == 0) {
|
||||
queue_type = g_type_register_static(G_TYPE_OBJECT,
|
||||
"GuiQueueModel",
|
||||
&queue_type_info,
|
||||
(GTypeFlags)0);
|
||||
g_type_add_interface_static(queue_type,
|
||||
GTK_TYPE_TREE_MODEL,
|
||||
&queue_tree_model);
|
||||
}
|
||||
|
||||
return queue_type;
|
||||
}
|
||||
|
||||
void gui_queue_model_add(struct queue *queue, unsigned int row)
|
||||
void gui_model_add(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!queue_model || queue != queue_model->gqm_queue)
|
||||
if (cur_playlist != playlist)
|
||||
return;
|
||||
|
||||
path = gtk_tree_path_new_from_indices(row, -1);
|
||||
_queue_model_get_iter(GTK_TREE_MODEL(queue_model), &iter, path);
|
||||
gtk_tree_model_row_inserted(GTK_TREE_MODEL(queue_model), path, &iter);
|
||||
__gui_queue_model_set_runtime();
|
||||
path = gtk_tree_path_new_from_indices(0, -1);
|
||||
__gui_model_get_iter(GTK_TREE_MODEL(gui_model), &iter, path);
|
||||
gtk_tree_model_row_inserted(GTK_TREE_MODEL(gui_model), path, &iter);
|
||||
gtk_tree_path_free(path);
|
||||
|
||||
__gui_model_set_runtime();
|
||||
}
|
||||
|
||||
void gui_queue_model_remove(struct queue *queue, unsigned int row)
|
||||
{
|
||||
GtkTreePath *path;
|
||||
|
||||
if (queue != queue_model->gqm_queue)
|
||||
return;
|
||||
|
||||
path = gtk_tree_path_new_from_indices(row, -1);
|
||||
gtk_tree_model_row_deleted(GTK_TREE_MODEL(queue_model), path);
|
||||
__gui_queue_model_set_runtime();
|
||||
gtk_tree_path_free(path);
|
||||
}
|
||||
|
||||
void gui_queue_model_clear(struct queue *queue, unsigned int n)
|
||||
void gui_model_remove(struct playlist *playlist, struct track *track,
|
||||
unsigned int n)
|
||||
{
|
||||
GtkTreePath *path;
|
||||
unsigned int i;
|
||||
|
||||
if (queue != queue_model->gqm_queue)
|
||||
if (cur_playlist != playlist)
|
||||
return;
|
||||
|
||||
path = gtk_tree_path_new_from_indices(n - 1, -1);
|
||||
for (i = 0; i < n; i++) {
|
||||
gtk_tree_model_row_deleted(GTK_TREE_MODEL(queue_model), path);
|
||||
gtk_tree_model_row_deleted(GTK_TREE_MODEL(gui_model), path);
|
||||
gtk_tree_path_prev(path);
|
||||
}
|
||||
|
||||
__gui_queue_model_set_runtime();
|
||||
gtk_tree_path_free(path);
|
||||
|
||||
__gui_model_set_runtime();
|
||||
}
|
||||
|
||||
void gui_queue_model_update(struct queue *queue, unsigned int row)
|
||||
void gui_model_update(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter iter;
|
||||
if (cur_playlist == playlist)
|
||||
gtk_tree_model_foreach(GTK_TREE_MODEL(gui_model),
|
||||
__gui_model_foreach_changed, track);
|
||||
|
||||
if (queue != queue_model->gqm_queue)
|
||||
return;
|
||||
|
||||
path = gtk_tree_path_new_from_indices(row, -1);
|
||||
_queue_model_get_iter(GTK_TREE_MODEL(queue_model), &iter, path);
|
||||
gtk_tree_model_row_changed(GTK_TREE_MODEL(queue_model), path, &iter);
|
||||
__gui_queue_model_set_runtime();
|
||||
gtk_tree_path_free(path);
|
||||
__gui_model_set_runtime();
|
||||
}
|
||||
|
||||
void gui_queue_model_set_queue(struct queue *queue)
|
||||
void gui_model_set_playlist(struct playlist *playlist)
|
||||
{
|
||||
struct queue *cur = queue_model->gqm_queue;
|
||||
if (cur_playlist)
|
||||
gui_model_remove(cur_playlist, NULL, playlist_size(cur_playlist));
|
||||
|
||||
if (cur)
|
||||
gui_queue_model_clear(cur, queue_size(cur));
|
||||
cur_playlist = playlist;
|
||||
__gui_model_set_runtime();
|
||||
|
||||
queue_model->gqm_queue = queue;
|
||||
__gui_queue_model_set_runtime();
|
||||
|
||||
if (queue && queue_size(queue) > 0)
|
||||
gui_queue_model_add(queue, 0);
|
||||
if (playlist && playlist_size(playlist) > 0)
|
||||
gui_model_add(playlist, 0);
|
||||
}
|
||||
|
||||
struct queue *gui_queue_model_get_queue(void)
|
||||
struct playlist *gui_model_get_playlist(void)
|
||||
{
|
||||
return queue_model->gqm_queue;
|
||||
return cur_playlist;
|
||||
}
|
||||
|
||||
struct track * gui_queue_model_path_get_track(GtkTreePath *path)
|
||||
struct track * gui_model_path_get_track(GtkTreePath *path)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
|
||||
_queue_model_get_iter(GTK_TREE_MODEL(queue_model), &iter, path);
|
||||
return gui_queue_model_iter_get_track(&iter);
|
||||
__gui_model_get_iter(GTK_TREE_MODEL(gui_model), &iter, path);
|
||||
return gui_model_iter_get_track(&iter);
|
||||
}
|
||||
|
||||
|
||||
static const GTypeInfo queue_type_info = {
|
||||
.class_size = sizeof(GuiQueueModelClass),
|
||||
.base_init = NULL,
|
||||
.base_finalize = NULL,
|
||||
.class_init = (GClassInitFunc)_queue_model_class_init,
|
||||
.class_finalize = NULL,
|
||||
.class_data = NULL,
|
||||
.instance_size = sizeof(GuiQueueModel),
|
||||
.n_preallocs = 0,
|
||||
.instance_init = (GInstanceInitFunc)_queue_model_init,
|
||||
};
|
||||
|
||||
static const GInterfaceInfo queue_tree_model = {
|
||||
.interface_init = (GInterfaceInitFunc)_queue_tree_model_init,
|
||||
.interface_finalize = NULL,
|
||||
.interface_data = NULL,
|
||||
};
|
||||
|
|
|
@ -2,27 +2,26 @@
|
|||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/core.h>
|
||||
#include <core/audio.h>
|
||||
#include <gui/audio.h>
|
||||
#include <gui/builder.h>
|
||||
#include <gui/collection.h>
|
||||
#include <gui/filter.h>
|
||||
#include <gui/idle.h>
|
||||
#include <gui/model.h>
|
||||
#include <gui/playlist.h>
|
||||
#include <gui/queue.h>
|
||||
#include <gui/sidebar.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/treeview.h>
|
||||
#include <gui/window.h>
|
||||
|
||||
#define OCARINA_FLAGS (G_APPLICATION_HANDLES_COMMAND_LINE)
|
||||
|
||||
static const GOptionEntry ocarina_options[] = {
|
||||
{ "next", 'n', 0, G_OPTION_ARG_NONE, NULL, "Play next track", NULL },
|
||||
{ "pause", 'P', 0, G_OPTION_ARG_NONE, NULL, "Pause playback", NULL },
|
||||
{ "play", 'p', 0, G_OPTION_ARG_NONE, NULL, "Start playback", NULL },
|
||||
{ "previous", 'N', 0, G_OPTION_ARG_NONE, NULL, "Play previous track", NULL },
|
||||
{ "toggle", 't', 0, G_OPTION_ARG_NONE, NULL, "Toggle playback state", NULL },
|
||||
{ "version", 'v', 0, G_OPTION_ARG_NONE, NULL, "Print version and exit", NULL },
|
||||
{ "next", 'n', 0, G_OPTION_ARG_NONE, NULL, "Play next track", NULL },
|
||||
{ "pause", 'P', 0, G_OPTION_ARG_NONE, NULL, "Pause playback", NULL },
|
||||
{ "play", 'p', 0, G_OPTION_ARG_NONE, NULL, "Start playback", NULL },
|
||||
{ "previous", 'N', 0, G_OPTION_ARG_NONE, NULL, "Play previous track", NULL },
|
||||
{ "sync", 's', 0, G_OPTION_ARG_NONE, NULL, "Don't run background tasks", NULL },
|
||||
{ "toggle", 't', 0, G_OPTION_ARG_NONE, NULL, "Toggle playback state", NULL },
|
||||
{ "version", 'v', 0, G_OPTION_ARG_NONE, NULL, "Print version and exit", NULL },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
|
@ -32,11 +31,7 @@ const static gchar *OCARINA_APP = "org.gtk.ocarina";
|
|||
const static gchar *OCARINA_APP = "org.gtk.ocarina-debug";
|
||||
#endif
|
||||
|
||||
struct core_init_data init_data = {
|
||||
&playlist_ops,
|
||||
&audio_ops,
|
||||
};
|
||||
|
||||
static enum idle_sync_t idle_sync = IDLE_ASYNC;
|
||||
static int startup_argc;
|
||||
static char **startup_argv;
|
||||
|
||||
|
@ -53,13 +48,14 @@ static gchar *find_file_path(const gchar *file)
|
|||
|
||||
static void __ocarina_activate(GApplication *application, gpointer data)
|
||||
{
|
||||
gtk_application_add_window(GTK_APPLICATION(application),
|
||||
GTK_WINDOW(gui_builder_widget("o_window")));
|
||||
gtk_application_add_window(GTK_APPLICATION(application), gui_window());
|
||||
}
|
||||
|
||||
static int __ocarina_local_options(GApplication *application,
|
||||
GVariantDict *options, gpointer data)
|
||||
{
|
||||
if (g_variant_dict_contains(options, "sync"))
|
||||
idle_sync = IDLE_SYNC;
|
||||
if (!g_variant_dict_contains(options, "version"))
|
||||
return -1;
|
||||
g_printf("Ocarina %s\n", get_version());
|
||||
|
@ -74,9 +70,16 @@ static int __ocarina_command_line(GApplication *application,
|
|||
gpointer data)
|
||||
{
|
||||
GVariantDict *options;
|
||||
gchar **args;
|
||||
|
||||
g_application_activate(application);
|
||||
|
||||
args = g_application_command_line_get_arguments(command, NULL);
|
||||
if (args && args[1]) {
|
||||
audio_load_filepath(args[1]);
|
||||
g_strfreev(args);
|
||||
}
|
||||
|
||||
options = g_application_command_line_get_options_dict(command);
|
||||
if (g_variant_dict_contains(options, "next"))
|
||||
audio_next();
|
||||
|
@ -101,13 +104,12 @@ static void __ocarina_startup(GApplication *application, gpointer data)
|
|||
gchar *icon = find_file_path("ocarina.png");
|
||||
|
||||
gui_builder_init(ui);
|
||||
core_init(&startup_argc, &startup_argv, &init_data);
|
||||
gui_queue_model_init();
|
||||
gui_view_init();
|
||||
gui_queue_init();
|
||||
core_init(&startup_argc, &startup_argv, &playlist_cb, &audio_cb, idle_sync);
|
||||
gui_window_init(icon);
|
||||
gui_model_init();
|
||||
gui_filter_init();
|
||||
gui_treeview_init();
|
||||
gui_sidebar_init();
|
||||
gui_collection_init();
|
||||
gui_playlist_init();
|
||||
gui_audio_init();
|
||||
|
||||
|
@ -120,10 +122,13 @@ static void __ocarina_startup(GApplication *application, gpointer data)
|
|||
static void __ocarina_shutdown(GApplication *application, gpointer data)
|
||||
{
|
||||
gui_idle_disable();
|
||||
gui_audio_deinit();
|
||||
core_deinit();
|
||||
|
||||
gui_treeview_deinit();
|
||||
gui_filter_deinit();
|
||||
gui_model_deinit();
|
||||
gui_window_deinit();
|
||||
gui_queue_model_deinit();
|
||||
gui_builder_deinit();
|
||||
}
|
||||
|
||||
|
|
590
gui/playlist.c
590
gui/playlist.c
|
@ -2,484 +2,258 @@
|
|||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/idle.h>
|
||||
#include <core/settings.h>
|
||||
#include <core/string.h>
|
||||
#include <gui/builder.h>
|
||||
#include <gui/filter.h>
|
||||
#include <gui/model.h>
|
||||
#include <gui/playlist.h>
|
||||
#include <gui/queue.h>
|
||||
#include <gui/treeview.h>
|
||||
#include <gui/sidebar.h>
|
||||
|
||||
enum playlist_sidebar_columns {
|
||||
P_SB_IMAGE,
|
||||
P_SB_NAME,
|
||||
P_SB_TYPE,
|
||||
static void (*update_size[PL_MAX_TYPE])(struct playlist *) = {
|
||||
[PL_SYSTEM] = gui_pl_system_update,
|
||||
[PL_ARTIST] = gui_pl_artist_update,
|
||||
[PL_LIBRARY] = gui_pl_library_update,
|
||||
[PL_USER] = gui_pl_user_update,
|
||||
};
|
||||
|
||||
static GtkTreeStore *p_store;
|
||||
static gchar *p_name = NULL;
|
||||
static bool p_filter_enable = true;
|
||||
static bool p_init_done = false;
|
||||
static void (*select_playlist[PL_MAX_TYPE])(struct playlist *) = {
|
||||
[PL_SYSTEM] = gui_pl_system_select,
|
||||
[PL_ARTIST] = gui_pl_artist_select,
|
||||
[PL_LIBRARY] = gui_pl_library_select,
|
||||
[PL_USER] = gui_pl_user_select,
|
||||
};
|
||||
|
||||
static void __playlist_update_sizes(struct queue *);
|
||||
|
||||
static inline void __playlist_filter_get_iter(GtkTreeIter *iter, GtkTreeIter *child)
|
||||
static inline void __gui_playlist_update_size(struct playlist *playlist)
|
||||
{
|
||||
gtk_tree_model_filter_convert_iter_to_child_iter(
|
||||
GTK_TREE_MODEL_FILTER(gui_builder_object("o_playlist_filter")),
|
||||
child, iter);
|
||||
}
|
||||
static inline enum playlist_type_t __playlist_type(GtkTreeIter *iter)
|
||||
{
|
||||
enum playlist_type_t type;
|
||||
|
||||
gtk_tree_model_get(GTK_TREE_MODEL(p_store), iter, P_SB_TYPE, &type, -1);
|
||||
return (type < PL_MAX_TYPE) ? type : PL_SYSTEM;
|
||||
update_size[playlist->pl_type](playlist);
|
||||
}
|
||||
|
||||
static void __playlist_set(GtkTreeIter *iter, const gchar *name,
|
||||
const gchar *image, enum playlist_type_t type)
|
||||
static void __gui_playlist_alloc(struct playlist *playlist)
|
||||
{
|
||||
gtk_tree_store_set(p_store, iter, P_SB_NAME, name,
|
||||
P_SB_IMAGE, image,
|
||||
P_SB_TYPE, type, -1);
|
||||
if (playlist->pl_type == PL_ARTIST)
|
||||
gui_pl_artist_add(playlist);
|
||||
}
|
||||
|
||||
static void __playlist_set_size(GtkTreeIter *iter, const gchar *name)
|
||||
static void __gui_playlist_added(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(p_store), iter);
|
||||
enum playlist_type_t type = __playlist_type(iter);
|
||||
unsigned int size = playlist_size(type, name);
|
||||
const gchar *fmt = "%s\n%d track%s";
|
||||
gchar *text;
|
||||
|
||||
if (settings_get("core.playlist.cur.type") == type &&
|
||||
settings_get("core.playlist.cur.id") == playlist_get_id(type, name))
|
||||
fmt = "<b>%s\n%d track%s</b>";
|
||||
|
||||
text = g_markup_printf_escaped(fmt, name, size, (size == 1) ? "" : "s");
|
||||
gtk_tree_store_set(p_store, iter, P_SB_NAME, text, -1);
|
||||
|
||||
gtk_tree_path_free(path);
|
||||
g_free(text);
|
||||
gui_model_add(playlist, track);
|
||||
gui_filter_refilter(playlist);
|
||||
__gui_playlist_update_size(playlist);
|
||||
}
|
||||
|
||||
static void __playlist_add(GtkTreeIter *parent, const gchar *name,
|
||||
const gchar *image, enum playlist_type_t type)
|
||||
static void __gui_playlist_removed(struct playlist *playlist, struct track *track,
|
||||
unsigned int n)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
gtk_tree_store_insert(p_store, &iter, parent, -1);
|
||||
__playlist_set(&iter, name, image, type);
|
||||
__playlist_set_size(&iter, name);
|
||||
gui_model_remove(playlist, track, n);
|
||||
__gui_playlist_update_size(playlist);
|
||||
}
|
||||
|
||||
static gchar *__playlist_name(GtkTreeIter *iter)
|
||||
|
||||
struct playlist_callbacks playlist_cb = {
|
||||
.pl_cb_alloc = __gui_playlist_alloc,
|
||||
.pl_cb_added = __gui_playlist_added,
|
||||
.pl_cb_removed = __gui_playlist_removed,
|
||||
.pl_cb_updated = gui_model_update,
|
||||
};
|
||||
|
||||
|
||||
static void __gui_playlist_add_selected_to(struct playlist *playlist)
|
||||
{
|
||||
GtkTreeModel *model = GTK_TREE_MODEL(p_store);
|
||||
gchar *text, *parsed, *name, **split;
|
||||
GList *cur, *list = NULL;
|
||||
struct track *track;
|
||||
|
||||
gtk_tree_model_get(model, iter, P_SB_NAME, &text, -1);
|
||||
if (!text)
|
||||
return NULL;
|
||||
pango_parse_markup(text, -1, 0, NULL, &parsed, NULL, NULL);
|
||||
split = g_strsplit(parsed, "\n", 2);
|
||||
name = g_strdup(split[0]);
|
||||
if (!playlist)
|
||||
return;
|
||||
|
||||
g_strfreev(split);
|
||||
g_free(text);
|
||||
g_free(parsed);
|
||||
return name;
|
||||
list = gui_treeview_list_selected_tracks();
|
||||
cur = g_list_first(list);
|
||||
while (cur) {
|
||||
track = (struct track *)cur->data;
|
||||
playlist_add(playlist, track);
|
||||
cur = g_list_next(cur);
|
||||
}
|
||||
g_list_free(list);
|
||||
}
|
||||
|
||||
void __playlist_selection_changed(GtkTreeSelection *selection, gpointer data)
|
||||
void __gui_playlist_add_favorites(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
GtkTreeModel *model = GTK_TREE_MODEL(p_store);
|
||||
GtkTreeIter iter, child;
|
||||
struct queue *queue;
|
||||
__gui_playlist_add_selected_to(playlist_lookup(PL_SYSTEM, "Favorites"));
|
||||
}
|
||||
|
||||
if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
|
||||
__playlist_filter_get_iter(&iter, &child);
|
||||
if (p_name)
|
||||
g_free(p_name);
|
||||
void __gui_playlist_add_hidden(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
__gui_playlist_add_selected_to(playlist_lookup(PL_SYSTEM, "Hidden"));
|
||||
}
|
||||
|
||||
p_name = __playlist_name(&child);
|
||||
queue = playlist_get_queue(__playlist_type(&child), p_name);
|
||||
gui_queue_show(gui_queue(queue));
|
||||
void __gui_playlist_add_user(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
__gui_playlist_add_selected_to(gui_pl_user_add_dialog());
|
||||
}
|
||||
|
||||
void __gui_playlist_add_other(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
__gui_playlist_add_selected_to(data);
|
||||
}
|
||||
|
||||
void __gui_playlist_add_queued(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
__gui_playlist_add_selected_to(playlist_lookup(PL_SYSTEM, "Queued Tracks"));
|
||||
}
|
||||
|
||||
void __gui_playlist_delete(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
struct playlist *playlist = gui_model_get_playlist();
|
||||
GList *cur, *list = NULL;
|
||||
struct track *track;
|
||||
|
||||
if (!playlist)
|
||||
return;
|
||||
|
||||
list = gui_treeview_list_selected_tracks();
|
||||
cur = g_list_first(list);
|
||||
while (cur) {
|
||||
track = (struct track *)cur->data;
|
||||
playlist_remove(playlist, track);
|
||||
cur = g_list_next(cur);
|
||||
}
|
||||
g_list_free(list);
|
||||
}
|
||||
|
||||
void __gui_playlist_keypress(GtkTreeView *treeview, GdkEventKey *event,
|
||||
gpointer data)
|
||||
{
|
||||
switch (event->keyval) {
|
||||
case GDK_KEY_f:
|
||||
__gui_playlist_add_favorites(NULL, NULL);
|
||||
break;
|
||||
case GDK_KEY_h:
|
||||
__gui_playlist_add_hidden(NULL, NULL);
|
||||
break;
|
||||
case GDK_KEY_p:
|
||||
__gui_playlist_add_user(NULL, NULL);
|
||||
break;
|
||||
case GDK_KEY_q:
|
||||
__gui_playlist_add_queued(NULL, NULL);
|
||||
break;
|
||||
case GDK_KEY_Delete:
|
||||
__gui_playlist_delete(NULL, NULL);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean __playlist_on_select(GtkTreeSelection *selection, GtkTreeModel *model,
|
||||
GtkTreePath *path, gboolean selected, gpointer data)
|
||||
static GtkWidget *__gui_playlist_build_submenu(void)
|
||||
{
|
||||
struct queue *queue = NULL;
|
||||
GtkTreeIter iter, child;
|
||||
gchar *name;
|
||||
struct playlist *playlist;
|
||||
GList *list = gui_pl_user_list();
|
||||
GList *cur = g_list_first(list);
|
||||
GtkWidget *submenu, *item;
|
||||
|
||||
gtk_tree_model_get_iter(model, &iter, path);
|
||||
__playlist_filter_get_iter(&iter, &child);
|
||||
if (!list)
|
||||
return NULL;
|
||||
|
||||
name = __playlist_name(&child);
|
||||
queue = playlist_get_queue(__playlist_type(&child), name);
|
||||
g_free(name);
|
||||
submenu = gtk_menu_new();
|
||||
while (cur) {
|
||||
playlist = (struct playlist *)cur->data;
|
||||
item = gtk_menu_item_new_with_label(playlist->pl_name);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
||||
g_signal_connect(item, "activate",
|
||||
G_CALLBACK(__gui_playlist_add_other),
|
||||
playlist);
|
||||
cur = g_list_next(cur);
|
||||
}
|
||||
|
||||
return queue != NULL;
|
||||
gtk_widget_show_all(submenu);
|
||||
g_list_free(list);
|
||||
return submenu;
|
||||
}
|
||||
|
||||
bool __playlist_keypress(GtkTreeView *treeview, GdkEventKey *event,
|
||||
gpointer data)
|
||||
bool __gui_playlist_button_press(GtkTreeView *treeview, GdkEventButton *event,
|
||||
gpointer data)
|
||||
{
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
|
||||
GtkTreeModel *model = GTK_TREE_MODEL(p_store);
|
||||
GtkTreeIter iter, child;
|
||||
gchar *name = NULL;
|
||||
GtkTreePath *path;
|
||||
GList *rows;
|
||||
GtkWidget *submenu;
|
||||
|
||||
if (event->keyval != GDK_KEY_Delete)
|
||||
if (event->button != GDK_BUTTON_SECONDARY)
|
||||
return false;
|
||||
|
||||
rows = gtk_tree_selection_get_selected_rows(selection, &model);
|
||||
path = rows->data;
|
||||
submenu = __gui_playlist_build_submenu();
|
||||
gtk_menu_item_set_submenu(gui_rc_add_to_other(), submenu);
|
||||
gtk_widget_set_visible(GTK_WIDGET(gui_rc_add_to_other()),
|
||||
submenu != NULL);
|
||||
|
||||
if (!gtk_tree_model_get_iter(model, &iter, path))
|
||||
return false;
|
||||
|
||||
__playlist_filter_get_iter(&iter, &child);
|
||||
name = __playlist_name(&child);
|
||||
if (!name)
|
||||
goto out;
|
||||
|
||||
if (playlist_delete(__playlist_type(&child), name))
|
||||
gtk_tree_store_remove(p_store, &child);
|
||||
g_free(name);
|
||||
out:
|
||||
g_list_free_full(rows, (GDestroyNotify)gtk_tree_path_free);
|
||||
gui_treeview_select_path_at_pos(event->x, event->y);
|
||||
gtk_menu_popup_at_pointer(gui_rc_menu(), (GdkEvent *)event);
|
||||
return true;
|
||||
}
|
||||
|
||||
void __playlist_row_activated(GtkTreeView *treeview, GtkTreePath *path,
|
||||
GtkTreeViewColumn *col, gpointer data)
|
||||
void __gui_playlist_row_activated(GtkTreeView *treeview, GtkTreePath *path,
|
||||
GtkTreeViewColumn *col, gpointer data)
|
||||
{
|
||||
enum playlist_type_t type;
|
||||
GtkTreeIter iter, child;
|
||||
GtkTreeModel *model;
|
||||
struct queue *queue;
|
||||
unsigned int id;
|
||||
gchar *name;
|
||||
struct playlist *prev = playlist_current();
|
||||
|
||||
type = settings_get("core.playlist.cur.type");
|
||||
id = settings_get("core.playlist.cur.id");
|
||||
|
||||
model = GTK_TREE_MODEL(gui_builder_object("o_playlist_filter"));
|
||||
gtk_tree_model_get_iter(model, &iter, path);
|
||||
__playlist_filter_get_iter(&iter, &child);
|
||||
|
||||
name = __playlist_name(&child);
|
||||
playlist_select(__playlist_type(&child), name);
|
||||
__playlist_set_size(&child, name);
|
||||
g_free(name);
|
||||
|
||||
name = playlist_get_name(type, id);
|
||||
queue = playlist_get_queue(type, name);
|
||||
__playlist_update_sizes(queue);
|
||||
g_free(name);
|
||||
gui_sidebar_filter_path_select(path);
|
||||
__gui_playlist_update_size(prev);
|
||||
__gui_playlist_update_size(playlist_current());
|
||||
}
|
||||
|
||||
static gboolean __playlist_visible_func(GtkTreeModel *model, GtkTreeIter *iter,
|
||||
gpointer data)
|
||||
void __gui_playlist_row_collapsed(GtkTreeView *treeview, GtkTreeIter *iter,
|
||||
GtkTreePath *path, gpointer data)
|
||||
{
|
||||
gchar *name;
|
||||
bool show;
|
||||
|
||||
if (!p_filter_enable)
|
||||
return true;
|
||||
|
||||
name = __playlist_name(iter);
|
||||
show = __playlist_type(iter) == PL_USER ||
|
||||
!playlist_get_queue(__playlist_type(iter), name) ||
|
||||
(playlist_size(__playlist_type(iter), name) > 0);
|
||||
g_free(name);
|
||||
return show;
|
||||
gui_sidebar_filter_row_expanded(iter, false);
|
||||
}
|
||||
|
||||
static bool __playlist_queue_set_size(struct queue *queue, GtkTreeIter *iter)
|
||||
void __gui_playlist_row_expanded(GtkTreeView *treeview, GtkTreeIter *iter,
|
||||
GtkTreePath *path, gpointer data)
|
||||
{
|
||||
struct gui_queue *gq = gui_queue(queue);
|
||||
bool match = (__playlist_type(iter) == gq->gq_playlist->pl_type);
|
||||
gchar *name = __playlist_name(iter);
|
||||
|
||||
if (match)
|
||||
match = string_match(name, gq->gq_text);
|
||||
if (match)
|
||||
__playlist_set_size(iter, name);
|
||||
g_free(name);
|
||||
|
||||
return match;
|
||||
gui_sidebar_filter_row_expanded(iter, true);
|
||||
}
|
||||
|
||||
static void __playlist_update_sizes(struct queue *queue)
|
||||
void __gui_playlist_drag_data_received(GtkTreeView *treeview, GdkDragContext *context,
|
||||
gint x, gint y, GtkSelectionData *data,
|
||||
guint info, guint time, gpointer user_data)
|
||||
{
|
||||
GtkTreeModel *model = GTK_TREE_MODEL(p_store);
|
||||
GtkTreeModelFilter *filter;
|
||||
GtkTreeIter parent, iter;
|
||||
struct playlist *playlist;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!gtk_tree_model_get_iter_first(model, &parent))
|
||||
return;
|
||||
if (gui_sidebar_iter_from_xy(x, y, &iter))
|
||||
playlist = gui_sidebar_iter_playlist(&iter);
|
||||
if (!playlist)
|
||||
playlist = gui_pl_user_add_dialog();
|
||||
if (!playlist)
|
||||
goto out;
|
||||
|
||||
do {
|
||||
if (__playlist_queue_set_size(queue, &parent))
|
||||
goto out;
|
||||
if (gtk_tree_model_iter_children(model, &iter, &parent)) {
|
||||
do {
|
||||
if (__playlist_queue_set_size(queue, &iter))
|
||||
goto out;
|
||||
} while (gtk_tree_model_iter_next(model, &iter));
|
||||
}
|
||||
} while (gtk_tree_model_iter_next(model, &parent));
|
||||
if (playlist == playlist_lookup(PL_SYSTEM, "Collection") &&
|
||||
gui_model_get_playlist() == playlist_lookup(PL_SYSTEM, "Hidden"))
|
||||
__gui_playlist_delete(NULL, NULL);
|
||||
else if (playlist != playlist_lookup(PL_SYSTEM, "History"))
|
||||
__gui_playlist_add_selected_to(playlist);
|
||||
|
||||
out:
|
||||
filter = GTK_TREE_MODEL_FILTER(gui_builder_object("o_playlist_filter"));
|
||||
gtk_tree_model_filter_refilter(filter);
|
||||
}
|
||||
|
||||
static void *__playlist_init(struct queue *queue, void *data)
|
||||
{
|
||||
struct playlist *playlist = (struct playlist *)data;
|
||||
unsigned int flags = 0;
|
||||
|
||||
if (!string_match(playlist->pl_name, "History"))
|
||||
flags = GQ_CAN_RANDOM;
|
||||
if (p_init_done && playlist->pl_type == PL_ARTIST) {
|
||||
p_filter_enable = false;
|
||||
gui_playlist_add_artist(artist_find(playlist->pl_name));
|
||||
p_filter_enable = true;
|
||||
}
|
||||
if (p_init_done && playlist->pl_type == PL_USER)
|
||||
gui_playlist_add_user(playlist);
|
||||
return gui_queue_alloc(playlist, queue, playlist->pl_name, flags);
|
||||
}
|
||||
|
||||
static void __playlist_added(struct queue *queue, unsigned int row)
|
||||
{
|
||||
gui_queue_model_add(queue, row);
|
||||
__playlist_update_sizes(queue);
|
||||
}
|
||||
|
||||
static void __playlist_removed(struct queue *queue, unsigned int row)
|
||||
{
|
||||
gui_queue_model_remove(queue, row);
|
||||
__playlist_update_sizes(queue);
|
||||
}
|
||||
|
||||
static void __playlist_cleared(struct queue *queue, unsigned int n)
|
||||
{
|
||||
gui_queue_model_clear(queue, n);
|
||||
__playlist_update_sizes(queue);
|
||||
}
|
||||
|
||||
static bool __playlist_erase(struct queue *queue, struct track *track)
|
||||
{
|
||||
enum playlist_type_t type = gui_queue(queue)->gq_playlist->pl_type;
|
||||
const gchar *name = gui_queue(queue)->gq_playlist->pl_name;
|
||||
|
||||
switch (type) {
|
||||
case PL_SYSTEM:
|
||||
if (string_match(name, "Collection"))
|
||||
name = "Hidden";
|
||||
else if (!string_match(name, "Favorites") &&
|
||||
!string_match(name, "Hidden") &&
|
||||
!string_match(name, "Queued Tracks"))
|
||||
break;
|
||||
case PL_USER:
|
||||
playlist_remove(type, name, track);
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return false;
|
||||
g_signal_stop_emission_by_name(treeview, "drag_data_received");
|
||||
gtk_drag_finish(context, true, true, time);
|
||||
}
|
||||
|
||||
bool __gui_playlist_init_idle()
|
||||
{
|
||||
struct db_entry *dbe, *next;
|
||||
GtkTreeSelection *selection;
|
||||
GtkTreeModel *filter;
|
||||
GtkTreeIter iter;
|
||||
struct playlist *playlist = playlist_current();
|
||||
GtkTreeModel *filter = GTK_TREE_MODEL(gui_sidebar_filter());
|
||||
GtkTreeIter iter;
|
||||
|
||||
filter = GTK_TREE_MODEL(gui_builder_object("o_playlist_filter"));
|
||||
selection = gtk_tree_view_get_selection(
|
||||
GTK_TREE_VIEW(gui_builder_widget("o_playlist_view")));
|
||||
gtk_tree_model_get_iter_first(filter, &iter);
|
||||
gtk_tree_selection_select_iter(selection, &iter);
|
||||
do {
|
||||
gui_sidebar_filter_set_expand(&iter);
|
||||
} while (gtk_tree_model_iter_next(filter, &iter));
|
||||
|
||||
p_filter_enable = false;
|
||||
db_for_each(dbe, next, artist_db_get())
|
||||
gui_playlist_add_artist(ARTIST(dbe));
|
||||
|
||||
db_for_each(dbe, next, pl_user_db_get())
|
||||
gui_playlist_add_user(&USER_PLAYLIST(dbe)->pl_playlist);
|
||||
p_filter_enable = true;
|
||||
|
||||
p_init_done = true;
|
||||
select_playlist[playlist->pl_type](playlist);
|
||||
return true;
|
||||
}
|
||||
|
||||
void gui_playlist_init()
|
||||
{
|
||||
GtkTreeModelFilter *filter;
|
||||
GtkTreeView *treeview;
|
||||
GtkTreeIter parent;
|
||||
|
||||
filter = GTK_TREE_MODEL_FILTER(gui_builder_object("o_playlist_filter"));
|
||||
p_store = GTK_TREE_STORE(gui_builder_object("o_playlist_store"));
|
||||
treeview = GTK_TREE_VIEW(gui_builder_widget("o_playlist_view"));
|
||||
|
||||
gtk_tree_model_filter_set_visible_func(filter, __playlist_visible_func,
|
||||
NULL, NULL);
|
||||
|
||||
gtk_tree_store_insert(p_store, &parent, NULL, -1);
|
||||
__playlist_set(&parent, "Queued Tracks", "audio-x-generic", PL_SYSTEM);
|
||||
__playlist_set_size(&parent, "Queued Tracks");
|
||||
|
||||
gtk_tree_store_insert(p_store, &parent, NULL, -1);
|
||||
__playlist_set(&parent, "Collection", "media-optical", PL_SYSTEM);
|
||||
__playlist_set_size(&parent, "Collection");
|
||||
|
||||
gtk_tree_store_insert(p_store, &parent, NULL, -1);
|
||||
__playlist_set(&parent, "History", "document-open-recent", PL_SYSTEM);
|
||||
__playlist_set_size(&parent, "History");
|
||||
|
||||
/* Add "Playlists" header and playlists. */
|
||||
gtk_tree_store_insert(p_store, &parent, NULL, -1);
|
||||
gtk_tree_store_insert(p_store, &parent, NULL, -1);
|
||||
__playlist_set(&parent, "<big>Playlists</big>", "emblem-documents", 0);
|
||||
__playlist_add(&parent, "Favorites", "emblem-favorite", PL_SYSTEM);
|
||||
__playlist_add(&parent, "Hidden", "window-close", PL_SYSTEM);
|
||||
|
||||
/* Add "Dynamic" header. */
|
||||
gtk_tree_store_insert(p_store, &parent, NULL, -1);
|
||||
gtk_tree_store_insert(p_store, &parent, NULL, -1);
|
||||
__playlist_set(&parent, "<big>Dynamic</big>", "emblem-system", 0);
|
||||
|
||||
/* Add playlists. */
|
||||
__playlist_add(&parent, "Most Played", "go-up", PL_SYSTEM);
|
||||
__playlist_add(&parent, "Least Played", "go-down", PL_SYSTEM);
|
||||
__playlist_add(&parent, "Unplayed", "audio-x-generic", PL_SYSTEM);
|
||||
|
||||
/* Add "Library" header. */
|
||||
gtk_tree_store_insert(p_store, &parent, NULL, -1);
|
||||
gtk_tree_store_insert(p_store, &parent, NULL, -1);
|
||||
__playlist_set(&parent, "<big>Library</big>", "system-file-manager", 0);
|
||||
|
||||
gtk_tree_view_expand_all(treeview);
|
||||
gtk_tree_selection_set_select_function(
|
||||
gtk_tree_view_get_selection(treeview),
|
||||
__playlist_on_select, NULL, NULL);
|
||||
gui_pl_system_init();
|
||||
gui_pl_artist_init();
|
||||
gui_pl_user_init();
|
||||
gui_pl_library_init();
|
||||
|
||||
idle_schedule(IDLE_SYNC, __gui_playlist_init_idle, NULL);
|
||||
}
|
||||
|
||||
gchar *gui_playlist_cur()
|
||||
{
|
||||
return p_name;
|
||||
}
|
||||
|
||||
void gui_playlist_add_library(struct library *library)
|
||||
{
|
||||
GtkTreeIter parent;
|
||||
gchar *name;
|
||||
|
||||
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(p_store), &parent);
|
||||
while (gtk_tree_model_iter_next(GTK_TREE_MODEL(p_store), &parent)) {
|
||||
name = __playlist_name(&parent);
|
||||
if (string_match(name, "Library"))
|
||||
__playlist_add(&parent, library->li_path, "folder", PL_LIBRARY);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
gtk_tree_view_expand_all(
|
||||
GTK_TREE_VIEW(gui_builder_widget("o_playlist_view")));
|
||||
}
|
||||
|
||||
void gui_playlist_add_artist(struct artist *artist)
|
||||
{
|
||||
GtkTreeIter parent, sibling, iter;
|
||||
gchar *name;
|
||||
bool match;
|
||||
|
||||
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(p_store), &parent);
|
||||
do {
|
||||
name = __playlist_name(&parent);
|
||||
match = string_match(name, "Collection");
|
||||
g_free(name);
|
||||
|
||||
if (match)
|
||||
break;
|
||||
} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(p_store), &parent));
|
||||
|
||||
if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(p_store), &sibling, &parent)) {
|
||||
__playlist_add(&parent, artist->ar_name, "system-users", PL_ARTIST);
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
name = __playlist_name(&sibling);
|
||||
match = g_utf8_collate(name, artist->ar_name) >= 0;
|
||||
g_free(name);
|
||||
|
||||
if (match) {
|
||||
gtk_tree_store_insert_before(p_store, &iter, &parent, &sibling);
|
||||
__playlist_set(&iter, artist->ar_name, "system-users", PL_ARTIST);
|
||||
__playlist_set_size(&iter, artist->ar_name);
|
||||
return;
|
||||
}
|
||||
} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(p_store), &sibling));
|
||||
|
||||
__playlist_add(&parent, artist->ar_name, "system-users", PL_ARTIST);
|
||||
}
|
||||
|
||||
void gui_playlist_add_user(struct playlist *playlist)
|
||||
{
|
||||
GtkTreeIter parent, sibling, iter;
|
||||
gchar *name;
|
||||
bool match;
|
||||
|
||||
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(p_store), &parent);
|
||||
do {
|
||||
name = __playlist_name(&parent);
|
||||
match = string_match(name, "Playlists");
|
||||
g_free(name);
|
||||
|
||||
if (match)
|
||||
break;
|
||||
} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(p_store), &parent));
|
||||
|
||||
if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(p_store), &sibling, &parent)) {
|
||||
__playlist_add(&parent, playlist->pl_name, "text-x-generic", PL_USER);
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
name = __playlist_name(&sibling);
|
||||
match = g_utf8_collate(name, playlist->pl_name) >= 0;
|
||||
match = match && !(__playlist_type(&sibling) == PL_SYSTEM);
|
||||
g_free(name);
|
||||
|
||||
if (match) {
|
||||
gtk_tree_store_insert_before(p_store, &iter, &parent, &sibling);
|
||||
__playlist_set(&iter, playlist->pl_name, "text-x-generic", PL_USER);
|
||||
__playlist_set_size(&iter, playlist->pl_name);
|
||||
return;
|
||||
}
|
||||
} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(p_store), &sibling));
|
||||
|
||||
__playlist_add(&parent, playlist->pl_name, "text-x-generic", PL_USER);
|
||||
}
|
||||
|
||||
struct queue_ops playlist_ops = {
|
||||
.qop_init = __playlist_init,
|
||||
.qop_deinit = gui_queue_free,
|
||||
.qop_added = __playlist_added,
|
||||
.qop_erase = __playlist_erase,
|
||||
.qop_removed = __playlist_removed,
|
||||
.qop_cleared = __playlist_cleared,
|
||||
.qop_updated = gui_queue_model_update,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/idle.h>
|
||||
#include <core/playlist.h>
|
||||
#include <gui/sidebar.h>
|
||||
|
||||
static bool __gui_pl_artist_header(GtkTreeIter *iter)
|
||||
{
|
||||
if (gui_sidebar_iter_first(iter))
|
||||
return gui_sidebar_iter_find(iter, "Collection", PL_SYSTEM);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool __gui_pl_artist_init_idle()
|
||||
{
|
||||
struct db_entry *artist, *next;
|
||||
struct playlist *playlist;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!__gui_pl_artist_header(&iter))
|
||||
return false;
|
||||
|
||||
db_for_each(artist, next, artist_db_get()) {
|
||||
playlist = ARTIST(artist)->ar_playlist;
|
||||
gui_sidebar_iter_sort_child(&iter, playlist, "system-users");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void gui_pl_artist_init()
|
||||
{
|
||||
idle_schedule(IDLE_SYNC, __gui_pl_artist_init_idle, NULL);
|
||||
}
|
||||
|
||||
bool gui_pl_artist_add(struct playlist *playlist)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!__gui_pl_artist_header(&iter))
|
||||
return false;
|
||||
|
||||
gui_sidebar_iter_sort_child(&iter, playlist, "system-users");
|
||||
return true;
|
||||
}
|
||||
|
||||
void gui_pl_artist_update(struct playlist *playlist)
|
||||
{
|
||||
GtkTreeIter iter, child;
|
||||
|
||||
if (!__gui_pl_artist_header(&iter))
|
||||
return;
|
||||
if (!gui_sidebar_iter_down(&iter, &child))
|
||||
return;
|
||||
if (gui_sidebar_iter_find(&child, playlist->pl_name, playlist->pl_type))
|
||||
gui_sidebar_iter_update(&child);
|
||||
}
|
||||
|
||||
void gui_pl_artist_select(struct playlist *playlist)
|
||||
{
|
||||
GtkTreeIter iter, child;
|
||||
|
||||
if (!__gui_pl_artist_header(&iter))
|
||||
return;
|
||||
if (!gui_sidebar_iter_down(&iter, &child))
|
||||
return;
|
||||
if (gui_sidebar_iter_find(&child, playlist->pl_name, playlist->pl_type))
|
||||
gui_sidebar_iter_select(&child);
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright 2015 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/idle.h>
|
||||
#include <core/playlist.h>
|
||||
#include <gui/idle.h>
|
||||
#include <gui/playlists/library.h>
|
||||
#include <gui/sidebar.h>
|
||||
#include <gui/window.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
void __gui_pl_library_choose(GtkButton *button, gpointer data)
|
||||
{
|
||||
GtkFileFilter *filter;
|
||||
const gchar *music;
|
||||
GtkWidget *dialog;
|
||||
gchar *path;
|
||||
gint res;
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_add_mime_type(filter, "inode/directory");
|
||||
|
||||
dialog = gtk_file_chooser_dialog_new("Add Music", gui_window(),
|
||||
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
_("_Open"), GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
|
||||
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
|
||||
|
||||
music = g_get_user_special_dir(G_USER_DIRECTORY_MUSIC);
|
||||
if (music)
|
||||
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
|
||||
music);
|
||||
|
||||
res = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
if (res == GTK_RESPONSE_ACCEPT) {
|
||||
path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
|
||||
gui_pl_library_add(path);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
gtk_widget_destroy(dialog);
|
||||
}
|
||||
|
||||
static bool __gui_pl_library_header(GtkTreeIter *iter)
|
||||
{
|
||||
if (gui_sidebar_iter_first(iter))
|
||||
return gui_sidebar_iter_find(iter, "Library", PL_MAX_TYPE);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool __gui_pl_library_init_idle()
|
||||
{
|
||||
struct db_entry *library, *next;
|
||||
struct playlist *playlist;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!__gui_pl_library_header(&iter))
|
||||
return false;
|
||||
|
||||
db_for_each(library, next, library_db_get()) {
|
||||
playlist = LIBRARY(library)->li_playlist;
|
||||
gui_sidebar_iter_sort_child(&iter, playlist, "folder");
|
||||
}
|
||||
|
||||
#ifndef CONFIG_TESTING
|
||||
if (library_db_get()->db_size == 0)
|
||||
__gui_pl_library_choose(NULL, NULL);
|
||||
#endif /* CONFIG_TESTING */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void gui_pl_library_init()
|
||||
{
|
||||
idle_schedule(IDLE_SYNC, __gui_pl_library_init_idle, NULL);
|
||||
}
|
||||
|
||||
struct playlist *gui_pl_library_add(const gchar *filename)
|
||||
{
|
||||
struct playlist *playlist;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!__gui_pl_library_header(&iter))
|
||||
return false;
|
||||
|
||||
playlist = playlist_new(PL_LIBRARY, filename);
|
||||
if (playlist) {
|
||||
gui_sidebar_iter_sort_child(&iter, playlist, "folder");
|
||||
gui_idle_enable();
|
||||
}
|
||||
return playlist;
|
||||
}
|
||||
|
||||
void gui_pl_library_update(struct playlist *playlist)
|
||||
{
|
||||
GtkTreeIter iter, child;
|
||||
|
||||
if (!__gui_pl_library_header(&iter))
|
||||
return;
|
||||
if (!gui_sidebar_iter_down(&iter, &child))
|
||||
return;
|
||||
if (gui_sidebar_iter_find(&child, playlist->pl_name, playlist->pl_type))
|
||||
gui_sidebar_iter_update(&child);
|
||||
}
|
||||
|
||||
void gui_pl_library_select(struct playlist *playlist)
|
||||
{
|
||||
GtkTreeIter iter, child;
|
||||
if (!__gui_pl_library_header(&iter))
|
||||
return;
|
||||
if (!gui_sidebar_iter_down(&iter, &child))
|
||||
return;
|
||||
if (gui_sidebar_iter_find(&child, playlist->pl_name, playlist->pl_type))
|
||||
gui_sidebar_iter_select(&child);
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/audio.h>
|
||||
#include <core/idle.h>
|
||||
#include <core/playlist.h>
|
||||
#include <core/string.h>
|
||||
#include <gui/playlists/system.h>
|
||||
#include <gui/sidebar.h>
|
||||
|
||||
static struct playlist *favorites;
|
||||
static struct playlist *hidden;
|
||||
|
||||
static bool __gui_pl_system_is_playlist(struct playlist *playlist)
|
||||
{
|
||||
return string_match(playlist->pl_name, "Favorites") ||
|
||||
string_match(playlist->pl_name, "Hidden");
|
||||
}
|
||||
|
||||
static bool __gui_pl_system_is_dynamic(struct playlist *playlist)
|
||||
{
|
||||
return string_match(playlist->pl_name, "Most Played") ||
|
||||
string_match(playlist->pl_name, "Least Played") ||
|
||||
string_match(playlist->pl_name, "Unplayed");
|
||||
}
|
||||
|
||||
static bool __gui_pl_system_find_descend_header(GtkTreeIter *iter,
|
||||
const gchar *name)
|
||||
{
|
||||
GtkTreeIter header;
|
||||
|
||||
if (!gui_sidebar_iter_first(&header))
|
||||
return false;
|
||||
if (!gui_sidebar_iter_find(&header, name, PL_MAX_TYPE))
|
||||
return false;
|
||||
return gui_sidebar_iter_down(&header, iter);
|
||||
}
|
||||
|
||||
void __gui_pl_system_favorite_toggled(GtkToggleButton *toggle, gpointer data)
|
||||
{
|
||||
if (gtk_toggle_button_get_active(toggle))
|
||||
playlist_add(favorites, audio_cur_track());
|
||||
else
|
||||
playlist_remove(favorites, audio_cur_track());
|
||||
}
|
||||
|
||||
void __gui_pl_system_hide_toggled(GtkToggleButton *toggle, gpointer data)
|
||||
{
|
||||
if (gtk_toggle_button_get_active(toggle)) {
|
||||
if (playlist_add(hidden, audio_cur_track()))
|
||||
audio_next();
|
||||
} else
|
||||
playlist_remove(hidden, audio_cur_track());
|
||||
}
|
||||
|
||||
static bool __gui_pl_system_init_idle()
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
|
||||
/* Add toplevel playlists. */
|
||||
gui_sidebar_iter_first(&iter);
|
||||
gui_sidebar_iter_add(&iter, playlist_lookup(PL_SYSTEM, "Queued Tracks"),
|
||||
"audio-x-generic");
|
||||
gui_sidebar_iter_add(&iter, playlist_lookup(PL_SYSTEM, "Collection"),
|
||||
"media-optical");
|
||||
gui_sidebar_iter_add(&iter, playlist_lookup(PL_SYSTEM, "History"),
|
||||
"document-open-recent");
|
||||
|
||||
/* Add user-modifiable playlists. */
|
||||
gui_sidebar_iter_find(&iter, "Playlists", PL_MAX_TYPE);
|
||||
gui_sidebar_iter_append_child(&iter, playlist_lookup(PL_SYSTEM, "Favorites"),
|
||||
"emblem-favorite");
|
||||
gui_sidebar_iter_append_child(&iter, playlist_lookup(PL_SYSTEM, "Hidden"),
|
||||
"window-close");
|
||||
|
||||
/* Add dynamic playlists. */
|
||||
gui_sidebar_iter_find(&iter, "Dynamic", PL_MAX_TYPE);
|
||||
gui_sidebar_iter_append_child(&iter, playlist_lookup(PL_SYSTEM, "Most Played"),
|
||||
"go-up");
|
||||
gui_sidebar_iter_append_child(&iter, playlist_lookup(PL_SYSTEM, "Least Played"),
|
||||
"go-down");
|
||||
gui_sidebar_iter_append_child(&iter, playlist_lookup(PL_SYSTEM, "Unplayed"),
|
||||
"audio-x-generic");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void gui_pl_system_init()
|
||||
{
|
||||
favorites = playlist_lookup(PL_SYSTEM, "Favorites");
|
||||
hidden = playlist_lookup(PL_SYSTEM, "Hidden");
|
||||
idle_schedule(IDLE_SYNC, __gui_pl_system_init_idle, NULL);
|
||||
}
|
||||
|
||||
void gui_pl_system_update(struct playlist *playlist)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (__gui_pl_system_is_playlist(playlist)) {
|
||||
if (!__gui_pl_system_find_descend_header(&iter, "Playlists"))
|
||||
return;
|
||||
} else if (__gui_pl_system_is_dynamic(playlist)) {
|
||||
if (!__gui_pl_system_find_descend_header(&iter, "Dynamic"))
|
||||
return;
|
||||
} else {
|
||||
if (!gui_sidebar_iter_first(&iter))
|
||||
return;
|
||||
}
|
||||
|
||||
if (gui_sidebar_iter_find(&iter, playlist->pl_name, playlist->pl_type))
|
||||
gui_sidebar_iter_update(&iter);
|
||||
}
|
||||
|
||||
void gui_pl_system_select(struct playlist *playlist)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (__gui_pl_system_is_playlist(playlist)) {
|
||||
if (!__gui_pl_system_find_descend_header(&iter, "Playlists"))
|
||||
return;
|
||||
} else if (__gui_pl_system_is_dynamic(playlist)) {
|
||||
if (!__gui_pl_system_find_descend_header(&iter, "Dynamic"))
|
||||
return;
|
||||
} else {
|
||||
if (!gui_sidebar_iter_first(&iter))
|
||||
return;
|
||||
}
|
||||
|
||||
if (gui_sidebar_iter_find(&iter, playlist->pl_name, playlist->pl_type))
|
||||
gui_sidebar_iter_select(&iter);
|
||||
}
|
||||
|
||||
void gui_pl_system_track_loaded(struct track *track)
|
||||
{
|
||||
gtk_toggle_button_set_active(gui_favorite_button(),
|
||||
playlist_has(favorites, track));
|
||||
gtk_toggle_button_set_active(gui_hide_button(),
|
||||
playlist_has(hidden, track));
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/idle.h>
|
||||
#include <core/playlist.h>
|
||||
#include <gui/sidebar.h>
|
||||
#include <gui/window.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
static bool __gui_pl_user_header(GtkTreeIter *iter)
|
||||
{
|
||||
if (gui_sidebar_iter_first(iter))
|
||||
return gui_sidebar_iter_find(iter, "Playlists", PL_MAX_TYPE);
|
||||
return false;
|
||||
}
|
||||
|
||||
static gint __gui_pl_user_compare(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
struct playlist *pl_a = (struct playlist *)a;
|
||||
struct playlist *pl_b = (struct playlist *)b;
|
||||
return g_utf8_collate(pl_a->pl_name, pl_b->pl_name);
|
||||
}
|
||||
|
||||
static bool __gui_pl_user_init_idle()
|
||||
{
|
||||
struct db_entry *user, *next;
|
||||
struct playlist *playlist;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!__gui_pl_user_header(&iter))
|
||||
return false;
|
||||
|
||||
db_for_each(user, next, pl_user_db_get()) {
|
||||
playlist = &USER_PLAYLIST(user)->pl_playlist;
|
||||
gui_sidebar_iter_sort_child(&iter, playlist, "text-x-generic");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void __gui_pl_user_editing_started(GtkCellRenderer *renderer,
|
||||
GtkCellEditable *editable,
|
||||
gchar *path, gpointer data)
|
||||
{
|
||||
struct playlist *playlist;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!gui_sidebar_iter_from_string(path, &iter))
|
||||
return;
|
||||
|
||||
playlist = gui_sidebar_iter_playlist(&iter);
|
||||
if (GTK_IS_ENTRY(editable))
|
||||
gtk_entry_set_text(GTK_ENTRY(editable), playlist->pl_name);
|
||||
}
|
||||
|
||||
void __gui_pl_user_edited(GtkCellRendererText *renderer, gchar *path,
|
||||
gchar *new_name, gpointer data)
|
||||
{
|
||||
struct playlist *playlist;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!gui_sidebar_iter_from_string(path, &iter))
|
||||
return;
|
||||
|
||||
playlist = gui_sidebar_iter_playlist(&iter);
|
||||
pl_user_rename(playlist, new_name);
|
||||
gui_sidebar_iter_update_playlist(&iter, playlist);
|
||||
gui_sidebar_iter_set_editable(&iter, false);
|
||||
}
|
||||
|
||||
void gui_pl_user_init()
|
||||
{
|
||||
idle_schedule(IDLE_SYNC, __gui_pl_user_init_idle, NULL);
|
||||
}
|
||||
|
||||
struct playlist *gui_pl_user_add(const gchar *name)
|
||||
{
|
||||
struct playlist *playlist;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!__gui_pl_user_header(&iter))
|
||||
return NULL;
|
||||
|
||||
playlist = playlist_new(PL_USER, name);
|
||||
if (playlist)
|
||||
gui_sidebar_iter_sort_child(&iter, playlist, "text-x-generic");
|
||||
return playlist;
|
||||
}
|
||||
|
||||
struct playlist *gui_pl_user_add_dialog(void)
|
||||
{
|
||||
unsigned int flags = GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL;
|
||||
GtkWidget *entry, *dialog, *content;
|
||||
struct playlist *playlist = NULL;
|
||||
|
||||
entry = gtk_entry_new();
|
||||
dialog = gtk_dialog_new_with_buttons("New Playlist Name?",
|
||||
gui_window(), flags,
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
_("_OK"), GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
|
||||
|
||||
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
|
||||
gtk_entry_set_activates_default(GTK_ENTRY(entry), true);
|
||||
gtk_container_add(GTK_CONTAINER(content), entry);
|
||||
gtk_widget_show_all(dialog);
|
||||
|
||||
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
|
||||
playlist = gui_pl_user_add(gtk_entry_get_text(GTK_ENTRY(entry)));
|
||||
gtk_widget_destroy(dialog);
|
||||
|
||||
return playlist;
|
||||
}
|
||||
|
||||
void gui_pl_user_update(struct playlist *playlist)
|
||||
{
|
||||
GtkTreeIter iter, child;
|
||||
|
||||
if (!__gui_pl_user_header(&iter))
|
||||
return;
|
||||
if (!gui_sidebar_iter_down(&iter, &child))
|
||||
return;
|
||||
if (gui_sidebar_iter_find(&child, playlist->pl_name, playlist->pl_type))
|
||||
gui_sidebar_iter_update(&child);
|
||||
}
|
||||
|
||||
void gui_pl_user_select(struct playlist *playlist)
|
||||
{
|
||||
GtkTreeIter iter, child;
|
||||
|
||||
if (!__gui_pl_user_header(&iter))
|
||||
return;
|
||||
if (!gui_sidebar_iter_down(&iter, &child))
|
||||
return;
|
||||
if (gui_sidebar_iter_find(&child, playlist->pl_name, playlist->pl_type))
|
||||
gui_sidebar_iter_select(&child);
|
||||
}
|
||||
|
||||
GList *gui_pl_user_list(void)
|
||||
{
|
||||
struct db_entry *user, *next;
|
||||
struct playlist *playlist;
|
||||
GList *list = NULL;
|
||||
|
||||
db_for_each(user, next, pl_user_db_get()) {
|
||||
playlist = &USER_PLAYLIST(user)->pl_playlist;
|
||||
list = g_list_insert_sorted(list, playlist,
|
||||
__gui_pl_user_compare);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
176
gui/queue.c
176
gui/queue.c
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/playlist.h>
|
||||
#include <core/string.h>
|
||||
#include <gui/builder.h>
|
||||
#include <gui/model.h>
|
||||
#include <gui/queue.h>
|
||||
#include <gui/view.h>
|
||||
|
||||
static struct gui_queue *gq_queue = NULL;
|
||||
|
||||
enum gq_filter_how {
|
||||
GQ_FILTER_DEFAULT,
|
||||
GQ_FILTER_ALBUM,
|
||||
GQ_FILTER_ARTIST,
|
||||
GQ_FILTER_GENRE,
|
||||
GQ_FILTER_TITLE,
|
||||
};
|
||||
|
||||
|
||||
static void __queue_toggle_flag(bool active, GtkWidget *widget,
|
||||
enum queue_flags flag)
|
||||
{
|
||||
if (gq_queue == NULL)
|
||||
return;
|
||||
|
||||
if (active)
|
||||
queue_set_flag(gq_queue->gq_queue, flag);
|
||||
else
|
||||
queue_unset_flag(gq_queue->gq_queue, flag);
|
||||
}
|
||||
|
||||
void __queue_random(GtkToggleButton *button, gpointer data)
|
||||
{
|
||||
__queue_toggle_flag(gtk_toggle_button_get_active(button),
|
||||
gtk_button_get_image(GTK_BUTTON(button)),
|
||||
Q_RANDOM);
|
||||
if (gq_queue && gq_queue->gq_playlist) {
|
||||
playlist_set_random(gq_queue->gq_playlist->pl_type,
|
||||
gq_queue->gq_playlist->pl_name,
|
||||
gtk_toggle_button_get_active(button));
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean __queue_visible_func(GtkTreeModel *model, GtkTreeIter *iter,
|
||||
gpointer data)
|
||||
{
|
||||
struct track *track;
|
||||
unsigned int i;
|
||||
gchar *token;
|
||||
bool match;
|
||||
|
||||
if (!gq_queue || !gq_queue->gq_search)
|
||||
return TRUE;
|
||||
|
||||
track = gui_queue_model_iter_get_track(iter);
|
||||
|
||||
for (i = 0; gq_queue->gq_search[i]; i++) {
|
||||
token = gq_queue->gq_search[i];
|
||||
|
||||
switch (gq_queue->gq_active) {
|
||||
case GQ_FILTER_ALBUM:
|
||||
match = album_match_token(track->tr_album, token);
|
||||
break;
|
||||
case GQ_FILTER_ARTIST:
|
||||
match = artist_match_token(track->tr_album->al_artist, token);
|
||||
break;
|
||||
case GQ_FILTER_GENRE:
|
||||
match = genre_match_token(track->tr_album->al_genre, token);
|
||||
break;
|
||||
case GQ_FILTER_TITLE:
|
||||
match = track_match_token(track, token);
|
||||
break;
|
||||
default: /* GQ_FILTER_DEFAULT */
|
||||
match = track_match_token(track, token) ||
|
||||
album_match_token(track->tr_album, token) ||
|
||||
artist_match_token(track->tr_album->al_artist, token) ||
|
||||
genre_match_token(track->tr_album->al_genre, token);
|
||||
}
|
||||
|
||||
if (!match)
|
||||
return false;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void __queue_filter(GtkSearchEntry *entry, gpointer data)
|
||||
{
|
||||
const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));
|
||||
GtkComboBox *combo = GTK_COMBO_BOX(gui_builder_widget("filter_how"));
|
||||
|
||||
if (!gq_queue)
|
||||
return;
|
||||
if (gq_queue->gq_search) {
|
||||
g_strfreev(gq_queue->gq_search);
|
||||
gq_queue->gq_search = NULL;
|
||||
}
|
||||
if (strlen(text) > 0) {
|
||||
gq_queue->gq_search = g_str_tokenize_and_fold(text, NULL, NULL);
|
||||
gq_queue->gq_active = gtk_combo_box_get_active(combo);
|
||||
}
|
||||
|
||||
gtk_tree_model_filter_refilter(gui_view_get_filter());
|
||||
gui_view_scroll();
|
||||
}
|
||||
|
||||
void __queue_filter_how_changed(int n)
|
||||
{
|
||||
__queue_filter(GTK_SEARCH_ENTRY(gui_builder_widget("o_search")), NULL);
|
||||
}
|
||||
|
||||
void gui_queue_init(void)
|
||||
{
|
||||
gtk_tree_model_filter_set_visible_func(gui_view_get_filter(),
|
||||
__queue_visible_func,
|
||||
gui_builder_object("o_search"),
|
||||
NULL);
|
||||
}
|
||||
|
||||
struct gui_queue *gui_queue_alloc(struct playlist *playlist, struct queue *queue,
|
||||
const gchar *text, unsigned int flags)
|
||||
{
|
||||
struct gui_queue *gq = g_malloc(sizeof(struct gui_queue));
|
||||
|
||||
gq->gq_flags = flags;
|
||||
gq->gq_text = g_strdup(text);
|
||||
gq->gq_search = NULL;
|
||||
gq->gq_playlist = playlist;
|
||||
gq->gq_queue = queue;
|
||||
|
||||
return gq;
|
||||
}
|
||||
|
||||
void gui_queue_free(struct queue *queue)
|
||||
{
|
||||
struct gui_queue *gq = gui_queue(queue);
|
||||
|
||||
queue->q_private = NULL;
|
||||
|
||||
if (gq_queue == gq)
|
||||
gui_view_set_queue(NULL);
|
||||
if (gq->gq_search)
|
||||
g_strfreev(gq->gq_search);
|
||||
g_free(gq->gq_text);
|
||||
g_free(gq);
|
||||
}
|
||||
|
||||
void gui_queue_show(struct gui_queue *queue)
|
||||
{
|
||||
GtkButton *random = GTK_BUTTON(gui_builder_widget("o_random"));
|
||||
GtkEntry *search = GTK_ENTRY(gui_builder_widget("o_search"));
|
||||
bool has_random = false;
|
||||
gchar *text;
|
||||
|
||||
gq_queue = queue;
|
||||
|
||||
gtk_widget_set_sensitive(GTK_WIDGET(random), gui_queue_can_random(queue));
|
||||
|
||||
if (queue) {
|
||||
has_random = queue_has_flag(queue->gq_queue, Q_RANDOM);
|
||||
gui_view_set_queue(queue->gq_queue);
|
||||
} else
|
||||
gui_view_set_queue(NULL);
|
||||
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(random), has_random);
|
||||
gtk_widget_set_sensitive(GTK_WIDGET(search), queue != NULL);
|
||||
|
||||
if (queue && queue->gq_search) {
|
||||
text = g_strjoinv(" ", queue->gq_search);
|
||||
gtk_entry_set_text(search, text);
|
||||
g_free(text);
|
||||
} else
|
||||
gtk_entry_set_text(search, "");
|
||||
}
|
495
gui/sidebar.c
495
gui/sidebar.c
|
@ -2,22 +2,507 @@
|
|||
* Copyright 2015 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/settings.h>
|
||||
#include <gui/builder.h>
|
||||
#include <core/string.h>
|
||||
#include <gui/model.h>
|
||||
#include <gui/sidebar.h>
|
||||
#include <gui/treeview.h>
|
||||
|
||||
enum sidebar_columns {
|
||||
SB_IMAGE,
|
||||
SB_NAME,
|
||||
SB_TYPE,
|
||||
SB_EDITABLE,
|
||||
};
|
||||
|
||||
const gchar *SIDEBAR_SETTING = "gui.sidebar.pos";
|
||||
|
||||
static gchar *__gui_sidebar_size_str(struct playlist *playlist)
|
||||
{
|
||||
const gchar *fmt = "%s\n%d track%s";
|
||||
unsigned int size;
|
||||
|
||||
void __sidebar_resize(GtkPaned *pane, GParamSpec *pspec, gpointer data)
|
||||
if (!playlist)
|
||||
return NULL;
|
||||
|
||||
size = playlist_size(playlist);
|
||||
if (playlist_current() == playlist)
|
||||
fmt = "<b>%s\n%d track%s</b>";
|
||||
return g_markup_printf_escaped(fmt, playlist->pl_name, size,
|
||||
(size != 1) ? "s" : "");
|
||||
}
|
||||
|
||||
static void __gui_sidebar_set(GtkTreeIter *iter, const gchar *name,
|
||||
const gchar *image, enum playlist_type_t type)
|
||||
{
|
||||
gtk_tree_store_set(gui_sidebar_store(), iter, SB_NAME, name,
|
||||
SB_IMAGE, image,
|
||||
SB_TYPE, type,
|
||||
SB_EDITABLE, false, -1);
|
||||
}
|
||||
|
||||
static void __gui_sidebar_set_playlist(GtkTreeIter *iter,
|
||||
struct playlist *playlist,
|
||||
const gchar *image)
|
||||
{
|
||||
gchar *text = __gui_sidebar_size_str(playlist);
|
||||
__gui_sidebar_set(iter, text, image, playlist->pl_type);
|
||||
g_free(text);
|
||||
}
|
||||
|
||||
static void __gui_sidebar_add_header(GtkTreeIter *iter, const gchar *name,
|
||||
const gchar *image)
|
||||
{
|
||||
gchar *formatted = g_strdup_printf("<big>%s</big>", name);
|
||||
|
||||
gtk_tree_store_insert(gui_sidebar_store(), iter, NULL, -1);
|
||||
__gui_sidebar_set(iter, NULL, NULL, PL_MAX_TYPE);
|
||||
gtk_tree_store_insert(gui_sidebar_store(), iter, NULL, -1);
|
||||
__gui_sidebar_set(iter, formatted, image, PL_MAX_TYPE);
|
||||
|
||||
g_free(formatted);
|
||||
}
|
||||
|
||||
static int __gui_sidebar_compare(GtkTreeIter *iter, const gchar *name,
|
||||
enum playlist_type_t type)
|
||||
{
|
||||
gchar *cur;
|
||||
int ret;
|
||||
|
||||
if (gui_sidebar_iter_type(iter) != type)
|
||||
return gui_sidebar_iter_type(iter) - type;
|
||||
|
||||
cur = gui_sidebar_iter_name(iter);
|
||||
ret = g_utf8_collate(cur, name);
|
||||
g_free(cur);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void __gui_sidebar_filter_iter_convert(GtkTreeIter *iter,
|
||||
GtkTreeIter *child)
|
||||
{
|
||||
gtk_tree_model_filter_convert_iter_to_child_iter(gui_sidebar_filter(),
|
||||
child, iter);
|
||||
}
|
||||
|
||||
static gchar *__gui_sidebar_filter_iter_name(GtkTreeIter *iter)
|
||||
{
|
||||
GtkTreeIter child;
|
||||
|
||||
__gui_sidebar_filter_iter_convert(iter, &child);
|
||||
return gui_sidebar_iter_name(&child);
|
||||
}
|
||||
|
||||
static gboolean __gui_sidebar_visible_func(GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer data)
|
||||
{
|
||||
enum playlist_type_t type = gui_sidebar_iter_type(iter);
|
||||
|
||||
if (type == PL_SYSTEM || type == PL_ARTIST)
|
||||
return playlist_size(gui_sidebar_iter_playlist(iter)) > 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean __gui_sidebar_can_select(GtkTreeSelection *selection,
|
||||
GtkTreeModel *model, GtkTreePath *path,
|
||||
gboolean selected, gpointer data)
|
||||
{
|
||||
GtkTreeIter iter, child;
|
||||
|
||||
gtk_tree_model_get_iter(model, &iter, path);
|
||||
__gui_sidebar_filter_iter_convert(&iter, &child);
|
||||
return gui_sidebar_iter_type(&child) != PL_MAX_TYPE;
|
||||
}
|
||||
|
||||
void __gui_sidebar_selection_changed(GtkTreeSelection *selection, gpointer data)
|
||||
{
|
||||
bool active = false, sensitive = false;
|
||||
struct playlist *playlist = NULL;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (gui_sidebar_iter_current(&iter)) {
|
||||
playlist = gui_sidebar_iter_playlist(&iter);
|
||||
active = playlist->pl_random;
|
||||
sensitive = (playlist->pl_ops->pl_set_random != NULL);
|
||||
}
|
||||
|
||||
gui_treeview_set_playlist(playlist);
|
||||
gtk_toggle_button_set_active(gui_random_button(), active);
|
||||
gtk_widget_set_sensitive(GTK_WIDGET(gui_random_button()), sensitive);
|
||||
}
|
||||
|
||||
static void __gui_sidebar_do_rename(GtkTreePath *path)
|
||||
{
|
||||
GtkTreeView *treeview = gui_sidebar_treeview();
|
||||
GtkTreeModel *model = gtk_tree_view_get_model(treeview);
|
||||
GtkTreeViewColumn *column = gtk_tree_view_get_column(treeview, SB_NAME);
|
||||
GtkTreeIter iter, child;
|
||||
|
||||
if (!gtk_tree_model_get_iter(model, &iter, path))
|
||||
return;
|
||||
|
||||
__gui_sidebar_filter_iter_convert(&iter, &child);
|
||||
gui_sidebar_iter_set_editable(&child, true);
|
||||
gtk_tree_view_set_cursor(treeview, path, column, true);
|
||||
}
|
||||
|
||||
static GtkTreePath *__gui_sidebar_current_path(void)
|
||||
{
|
||||
GtkTreeView *treeview = gui_sidebar_treeview();
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
|
||||
GtkTreeModel *model = gtk_tree_view_get_model(treeview);
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!gtk_tree_selection_get_selected(selection, &model, &iter))
|
||||
return NULL;
|
||||
return gtk_tree_model_get_path(model, &iter);
|
||||
}
|
||||
|
||||
bool __gui_sidebar_rename(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
GtkTreePath *path = __gui_sidebar_current_path();
|
||||
if (path) {
|
||||
__gui_sidebar_do_rename(path);
|
||||
gtk_tree_path_free(path);
|
||||
}
|
||||
return path != NULL;
|
||||
}
|
||||
|
||||
bool __gui_sidebar_select(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
GtkTreeView *treeview = gui_sidebar_treeview();
|
||||
GtkTreePath *path = __gui_sidebar_current_path();
|
||||
|
||||
if (path) {
|
||||
gtk_tree_view_row_activated(treeview, path, NULL);
|
||||
gtk_tree_path_free(path);
|
||||
}
|
||||
return path != NULL;
|
||||
}
|
||||
|
||||
bool __gui_sidebar_delete(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!gui_sidebar_iter_current(&iter))
|
||||
return false;
|
||||
if (playlist_delete(gui_model_get_playlist()))
|
||||
gtk_tree_store_remove(gui_sidebar_store(), &iter);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool __gui_sidebar_keypress(GtkTreeView *treeview, GdkEventKey *event,
|
||||
gpointer data)
|
||||
{
|
||||
switch (event->keyval) {
|
||||
case GDK_KEY_BackSpace:
|
||||
return __gui_sidebar_rename(NULL, NULL);
|
||||
case GDK_KEY_Return:
|
||||
return __gui_sidebar_select(NULL, NULL);
|
||||
case GDK_KEY_Delete:
|
||||
return __gui_sidebar_delete(NULL, NULL);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool __gui_sidebar_button_press(GtkTreeView *treeview, GdkEventButton *event,
|
||||
gpointer data)
|
||||
{
|
||||
enum playlist_type_t type = PL_MAX_TYPE;
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter iter;
|
||||
bool ret = true;
|
||||
|
||||
if (!gtk_tree_view_get_path_at_pos(treeview, event->x, event->y,
|
||||
&path, NULL, NULL, NULL))
|
||||
return false;
|
||||
|
||||
if (event->button == GDK_BUTTON_SECONDARY) {
|
||||
gtk_tree_view_set_cursor(treeview, path, NULL, false);
|
||||
if (gui_sidebar_iter_current(&iter))
|
||||
type = gui_sidebar_iter_type(&iter);
|
||||
gtk_widget_set_visible(gui_builder_widget("rc_sidebar_rename"),
|
||||
type == PL_USER);
|
||||
gtk_menu_popup_at_pointer(gui_sidebar_menu(), (GdkEvent *)event);
|
||||
} else if (event->type == GDK_2BUTTON_PRESS &&
|
||||
event->button == GDK_BUTTON_MIDDLE) {
|
||||
__gui_sidebar_do_rename(path);
|
||||
} else
|
||||
ret = false;
|
||||
|
||||
gtk_tree_path_free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __gui_sidebar_resized(GtkPaned *pane, GParamSpec *pspec, gpointer data)
|
||||
{
|
||||
settings_set(SIDEBAR_SETTING, gtk_paned_get_position(pane));
|
||||
}
|
||||
|
||||
void __gui_sidebar_random_toggled(GtkToggleButton *button, gpointer data)
|
||||
{
|
||||
struct playlist *playlist = gui_model_get_playlist();
|
||||
bool active = gtk_toggle_button_get_active(button);
|
||||
|
||||
if (playlist)
|
||||
playlist_set_random(playlist, active);
|
||||
}
|
||||
|
||||
void gui_sidebar_init()
|
||||
{
|
||||
GtkPaned *pane = GTK_PANED(gui_builder_widget("o_sidebar"));
|
||||
int pos = settings_get(SIDEBAR_SETTING);
|
||||
int pos = settings_get(SIDEBAR_SETTING);
|
||||
GtkTreeSelection *selection;
|
||||
GtkTreeIter iter;
|
||||
|
||||
gtk_tree_view_enable_model_drag_dest(gui_sidebar_treeview(),
|
||||
gui_model_drag_targets, gui_model_n_targets,
|
||||
GDK_ACTION_MOVE);
|
||||
|
||||
if (!gui_sidebar_iter_first(&iter)) {
|
||||
__gui_sidebar_add_header(&iter, "Playlists", "emblem-documents");
|
||||
__gui_sidebar_add_header(&iter, "Dynamic", "emblem-generic");
|
||||
__gui_sidebar_add_header(&iter, "Library", "emblem-system");
|
||||
|
||||
selection = gtk_tree_view_get_selection(gui_sidebar_treeview());
|
||||
gtk_tree_selection_set_select_function(selection,
|
||||
__gui_sidebar_can_select,
|
||||
NULL, NULL);
|
||||
gtk_tree_model_filter_set_visible_func(gui_sidebar_filter(),
|
||||
__gui_sidebar_visible_func,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
if (pos > 0)
|
||||
gtk_paned_set_position(pane, pos);
|
||||
gtk_paned_set_position(gui_sidebar(), pos);
|
||||
}
|
||||
|
||||
gboolean gui_sidebar_iter_current(GtkTreeIter *iter)
|
||||
{
|
||||
GtkTreeView *treeview = gui_sidebar_treeview();
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
|
||||
GtkTreeModel *model = GTK_TREE_MODEL(gui_sidebar_filter());
|
||||
GtkTreeIter it;
|
||||
|
||||
if (!gtk_tree_selection_get_selected(selection, &model, &it))
|
||||
return false;
|
||||
__gui_sidebar_filter_iter_convert(&it, iter);
|
||||
return true;
|
||||
}
|
||||
|
||||
gboolean gui_sidebar_iter_first(GtkTreeIter *iter)
|
||||
{
|
||||
return gtk_tree_model_get_iter_first(gui_sidebar_model(), iter);
|
||||
}
|
||||
|
||||
gboolean gui_sidebar_iter_next(GtkTreeIter *iter)
|
||||
{
|
||||
return gtk_tree_model_iter_next(gui_sidebar_model(), iter);
|
||||
}
|
||||
|
||||
gboolean gui_sidebar_iter_down(GtkTreeIter *iter, GtkTreeIter *child)
|
||||
{
|
||||
return gtk_tree_model_iter_children(gui_sidebar_model(), child, iter);
|
||||
}
|
||||
|
||||
gchar *gui_sidebar_iter_name(GtkTreeIter *iter)
|
||||
{
|
||||
gchar *text = NULL, *parsed = NULL, *name, **split;
|
||||
|
||||
gtk_tree_model_get(gui_sidebar_model(), iter, SB_NAME, &text, -1);
|
||||
if (!text)
|
||||
return g_strdup("");
|
||||
|
||||
pango_parse_markup(text, -1, 0, NULL, &parsed, NULL, NULL);
|
||||
if (!parsed)
|
||||
return g_strdup("");
|
||||
|
||||
split = g_strsplit(parsed, "\n", 2);
|
||||
name = g_strdup(split[0]);
|
||||
|
||||
g_strfreev(split);
|
||||
g_free(parsed);
|
||||
g_free(text);
|
||||
return name;
|
||||
}
|
||||
|
||||
enum playlist_type_t gui_sidebar_iter_type(GtkTreeIter *iter)
|
||||
{
|
||||
enum playlist_type_t type;
|
||||
gtk_tree_model_get(gui_sidebar_model(), iter, SB_TYPE, &type, -1);
|
||||
return type;
|
||||
}
|
||||
|
||||
bool gui_sidebar_iter_editable(GtkTreeIter *iter)
|
||||
{
|
||||
gboolean editable;
|
||||
gtk_tree_model_get(gui_sidebar_model(), iter, SB_EDITABLE, &editable, -1);
|
||||
return editable == TRUE;
|
||||
}
|
||||
|
||||
struct playlist *gui_sidebar_iter_playlist(GtkTreeIter *iter)
|
||||
{
|
||||
enum playlist_type_t type = gui_sidebar_iter_type(iter);
|
||||
gchar *name = gui_sidebar_iter_name(iter);
|
||||
struct playlist *playlist = playlist_lookup(type, name);
|
||||
|
||||
g_free(name);
|
||||
return playlist;
|
||||
}
|
||||
|
||||
void gui_sidebar_iter_add(GtkTreeIter *iter, struct playlist *playlist,
|
||||
const gchar *image)
|
||||
{
|
||||
GtkTreeIter new;
|
||||
gtk_tree_store_insert_before(gui_sidebar_store(), &new, NULL, iter);
|
||||
__gui_sidebar_set_playlist(&new, playlist, image);
|
||||
}
|
||||
|
||||
void gui_sidebar_iter_sort_child(GtkTreeIter *iter, struct playlist *playlist,
|
||||
const gchar *image)
|
||||
{
|
||||
GtkTreeIter child, new;
|
||||
|
||||
if (!gui_sidebar_iter_down(iter, &child))
|
||||
goto out_append;
|
||||
|
||||
do {
|
||||
if (__gui_sidebar_compare(&child, playlist->pl_name,
|
||||
playlist->pl_type) >= 0) {
|
||||
gtk_tree_store_insert_before(gui_sidebar_store(),
|
||||
&new, iter, &child);
|
||||
__gui_sidebar_set_playlist(&new, playlist, image);
|
||||
return;
|
||||
}
|
||||
} while (gui_sidebar_iter_next(&child));
|
||||
|
||||
out_append:
|
||||
gui_sidebar_iter_append_child(iter, playlist, image);
|
||||
}
|
||||
|
||||
void gui_sidebar_iter_append_child(GtkTreeIter *iter, struct playlist *playlist,
|
||||
const gchar *image)
|
||||
{
|
||||
GtkTreeIter new;
|
||||
gtk_tree_store_insert_before(gui_sidebar_store(), &new, iter, NULL);
|
||||
__gui_sidebar_set_playlist(&new, playlist, image);
|
||||
}
|
||||
|
||||
void gui_sidebar_iter_update_playlist(GtkTreeIter *iter,
|
||||
struct playlist *playlist)
|
||||
{
|
||||
gchar *text;
|
||||
|
||||
if (!playlist)
|
||||
return;
|
||||
|
||||
text = __gui_sidebar_size_str(playlist);
|
||||
gtk_tree_store_set(gui_sidebar_store(), iter, SB_NAME, text, -1);
|
||||
g_free(text);
|
||||
}
|
||||
|
||||
void gui_sidebar_iter_update(GtkTreeIter *iter)
|
||||
{
|
||||
gui_sidebar_iter_update_playlist(iter, gui_sidebar_iter_playlist(iter));
|
||||
}
|
||||
|
||||
void gui_sidebar_iter_select(GtkTreeIter *iter)
|
||||
{
|
||||
GtkTreeSelection *selection;
|
||||
GtkTreeIter filter;
|
||||
|
||||
gtk_tree_model_filter_convert_child_iter_to_iter(gui_sidebar_filter(),
|
||||
&filter, iter);
|
||||
|
||||
selection = gtk_tree_view_get_selection(gui_sidebar_treeview());
|
||||
gtk_tree_selection_select_iter(selection, &filter);
|
||||
}
|
||||
|
||||
bool gui_sidebar_iter_set_editable(GtkTreeIter *iter, bool editable)
|
||||
{
|
||||
enum playlist_type_t type = gui_sidebar_iter_type(iter);
|
||||
if (type != PL_USER)
|
||||
return false;
|
||||
gtk_tree_store_set(gui_sidebar_store(), iter, SB_EDITABLE, editable, -1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void gui_sidebar_filter_path_select(GtkTreePath *path)
|
||||
{
|
||||
GtkTreeModel *model = GTK_TREE_MODEL(gui_sidebar_filter());
|
||||
GtkTreeIter iter, child;
|
||||
|
||||
gtk_tree_model_get_iter(model, &iter, path);
|
||||
__gui_sidebar_filter_iter_convert(&iter, &child);
|
||||
|
||||
if (playlist_select(gui_sidebar_iter_playlist(&child)))
|
||||
gui_sidebar_iter_update(&child);
|
||||
}
|
||||
|
||||
void gui_sidebar_filter_set_expand(GtkTreeIter *iter)
|
||||
{
|
||||
gchar *name = __gui_sidebar_filter_iter_name(iter);
|
||||
gchar *setting = g_strdup_printf("gui.sidebar.expand.%s", name);
|
||||
GtkTreePath *path;
|
||||
|
||||
if (settings_get(setting) == true) {
|
||||
path = gtk_tree_model_get_path(
|
||||
GTK_TREE_MODEL(gui_sidebar_filter()), iter);
|
||||
|
||||
gtk_tree_view_expand_row(gui_sidebar_treeview(), path, false);
|
||||
gtk_tree_path_free(path);
|
||||
}
|
||||
|
||||
g_free(setting);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
void gui_sidebar_filter_row_expanded(GtkTreeIter *iter, bool expanded)
|
||||
{
|
||||
gchar *name = __gui_sidebar_filter_iter_name(iter);
|
||||
gchar *setting = g_strdup_printf("gui.sidebar.expand.%s", name);
|
||||
|
||||
settings_set(setting, expanded);
|
||||
|
||||
g_free(setting);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
gboolean gui_sidebar_iter_find(GtkTreeIter *iter, const gchar *name,
|
||||
enum playlist_type_t type)
|
||||
{
|
||||
do {
|
||||
if (__gui_sidebar_compare(iter, name, type) == 0)
|
||||
return TRUE;
|
||||
} while (gui_sidebar_iter_next(iter));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean gui_sidebar_iter_from_string(const gchar *path, GtkTreeIter *child)
|
||||
{
|
||||
GtkTreeModel *model = GTK_TREE_MODEL(gui_sidebar_filter());
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!gtk_tree_model_get_iter_from_string(model, &iter, path))
|
||||
return FALSE;
|
||||
__gui_sidebar_filter_iter_convert(&iter, child);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean gui_sidebar_iter_from_xy(gint x, gint y, GtkTreeIter *child)
|
||||
{
|
||||
GtkTreeModel *model = GTK_TREE_MODEL(gui_sidebar_filter());
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!gtk_tree_view_get_path_at_pos(gui_sidebar_treeview(), x, y,
|
||||
&path, NULL, NULL, NULL))
|
||||
return false;
|
||||
|
||||
gtk_tree_model_get_iter(model, &iter, path);
|
||||
__gui_sidebar_filter_iter_convert(&iter, child);
|
||||
gtk_tree_path_free(path);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/settings.h>
|
||||
#include <gui/filter.h>
|
||||
#include <gui/model.h>
|
||||
#include <gui/treeview.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct col_map_entry {
|
||||
enum compare_t compare;
|
||||
const gchar *setting;
|
||||
};
|
||||
|
||||
static const struct col_map_entry GUI_COL_MAP[GUI_MODEL_N_COLUMNS] = {
|
||||
[GUI_MODEL_TRACK_NR] = { COMPARE_TRACK, "gui.queue.track" },
|
||||
[GUI_MODEL_TITLE] = { COMPARE_TITLE, "gui.queue.title" },
|
||||
[GUI_MODEL_LENGTH] = { COMPARE_LENGTH, "gui.queue.length" },
|
||||
[GUI_MODEL_ARTIST] = { COMPARE_ARTIST, "gui.queue.artist" },
|
||||
[GUI_MODEL_ALBUM] = { COMPARE_ALBUM, "gui.queue.album" },
|
||||
[GUI_MODEL_YEAR] = { COMPARE_YEAR, "gui.queue.year" },
|
||||
[GUI_MODEL_GENRE] = { COMPARE_GENRE, "gui.queue.genre" },
|
||||
[GUI_MODEL_COUNT] = { COMPARE_COUNT, "gui.queue.count" },
|
||||
[GUI_MODEL_LAST_PLAY] = { COMPARE_PLAYED, NULL },
|
||||
};
|
||||
|
||||
static unsigned int sort_count = 0;
|
||||
static gchar *sort_text = NULL;
|
||||
static bool can_scroll = true;
|
||||
|
||||
static int __gui_treeview_colum_match_sort(enum compare_t compare)
|
||||
{
|
||||
struct playlist *playlist = gui_model_get_playlist();
|
||||
GSList *cur = playlist ? playlist->pl_sort : NULL;
|
||||
|
||||
while (cur) {
|
||||
int field = GPOINTER_TO_INT(cur->data);
|
||||
if (abs(field) == compare)
|
||||
return field;
|
||||
cur = g_slist_next(cur);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __gui_treeview_set_sort_indicators()
|
||||
{
|
||||
GtkTreeViewColumn *col;
|
||||
unsigned int i, order;
|
||||
int field;
|
||||
|
||||
for (i = 0; i < GUI_MODEL_N_COLUMNS; i++) {
|
||||
col = gtk_tree_view_get_column(gui_treeview(), i);
|
||||
if (!col)
|
||||
continue;
|
||||
|
||||
field = __gui_treeview_colum_match_sort(GUI_COL_MAP[i].compare);
|
||||
order = (field > 0) ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;
|
||||
|
||||
gtk_tree_view_column_set_sort_indicator(col, field != 0);
|
||||
gtk_tree_view_column_set_sort_order(col, order);
|
||||
}
|
||||
}
|
||||
|
||||
static void __gui_treeview_clear_sorting()
|
||||
{
|
||||
sort_count = 0;
|
||||
|
||||
if (sort_text) {
|
||||
g_free(sort_text);
|
||||
sort_text = NULL;
|
||||
gtk_label_set_text(gui_sorting(), "");
|
||||
}
|
||||
}
|
||||
|
||||
static void __gui_treeview_set_sorting(gchar *text)
|
||||
{
|
||||
gchar *formatted;
|
||||
|
||||
__gui_treeview_clear_sorting();
|
||||
if (!text)
|
||||
return;
|
||||
|
||||
sort_text = g_strdup(text);
|
||||
formatted = g_strdup_printf("Sorting: {%s}", text);
|
||||
gtk_label_set_text(gui_sorting(), formatted);
|
||||
g_free(formatted);
|
||||
}
|
||||
|
||||
static int __gui_treeview_dec_sort(gpointer data)
|
||||
{
|
||||
if (sort_count > 0)
|
||||
sort_count--;
|
||||
if (sort_count == 0)
|
||||
__gui_treeview_clear_sorting();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gchar *__gui_treeview_sort_text_append(GtkTreeViewColumn *col)
|
||||
{
|
||||
const gchar *title = gtk_tree_view_column_get_title(col);
|
||||
gchar *text, **split;
|
||||
unsigned int i;
|
||||
|
||||
if (!sort_text)
|
||||
return g_strdup(title);
|
||||
if (gtk_tree_view_column_get_sort_order(col) == GTK_SORT_ASCENDING)
|
||||
return g_strdup_printf("%s, %s", sort_text, title);
|
||||
|
||||
/* Find the column and prefix it with a minus sign */
|
||||
split = g_strsplit(sort_text, ", ", 0);
|
||||
for (i = 0; split[i] != NULL; i++) {
|
||||
if (g_strcmp0(split[i], title) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
g_free(split[i]);
|
||||
split[i] = g_strdup_printf("-%s", title);
|
||||
text = g_strjoinv(", ", split);
|
||||
g_strfreev(split);
|
||||
return text;
|
||||
}
|
||||
|
||||
static void __gui_treeview_column_clicked(GtkTreeViewColumn *col,
|
||||
gpointer data)
|
||||
{
|
||||
struct playlist *playlist = gui_model_get_playlist();
|
||||
enum compare_t compare = GPOINTER_TO_UINT(data);
|
||||
gchar *text;
|
||||
|
||||
if (!playlist)
|
||||
return;
|
||||
|
||||
if (sort_count == 0)
|
||||
playlist_clear_sort(playlist);
|
||||
if (!playlist_sort(playlist, compare))
|
||||
return;
|
||||
__gui_treeview_set_sort_indicators();
|
||||
|
||||
text = __gui_treeview_sort_text_append(col);
|
||||
__gui_treeview_set_sorting(text);
|
||||
g_free(text);
|
||||
|
||||
sort_count++;
|
||||
g_timeout_add_seconds(3, __gui_treeview_dec_sort, NULL);
|
||||
}
|
||||
|
||||
void __gui_treeview_column_resized(GtkTreeViewColumn *col, GParamSpec *pspec,
|
||||
gpointer data)
|
||||
{
|
||||
settings_set(GUI_COL_MAP[GPOINTER_TO_UINT(data)].setting,
|
||||
gtk_tree_view_column_get_width(col));
|
||||
}
|
||||
|
||||
void __gui_treeview_row_activated(GtkTreeView *treeview, GtkTreePath *path,
|
||||
GtkTreeViewColumn *col, gpointer data)
|
||||
{
|
||||
can_scroll = false;
|
||||
gui_filter_path_load_track(path);
|
||||
can_scroll = true;
|
||||
}
|
||||
|
||||
void __gui_treeview_drag_data_received(GtkTreeView *treeview, GdkDragContext *context,
|
||||
gint x, gint y, GtkSelectionData *data,
|
||||
guint info, guint time, gpointer user_data)
|
||||
{
|
||||
struct gui_model_drag_data *drag_data;
|
||||
unsigned int to, from;
|
||||
GtkTreePath *path;
|
||||
|
||||
drag_data = (void *)gtk_selection_data_get_data(data);
|
||||
if (gtk_tree_view_get_path_at_pos(gui_treeview(), x, y,
|
||||
&path, NULL, NULL, NULL))
|
||||
gtk_tree_path_prev(path);
|
||||
else if (!gtk_tree_view_get_visible_range(gui_treeview(), NULL, &path))
|
||||
return;
|
||||
|
||||
from = drag_data->drag_row;
|
||||
to = gui_filter_path_get_index(path);
|
||||
|
||||
if (playlist_rearrange(gui_model_get_playlist(), from, to)) {
|
||||
gtk_tree_selection_unselect_all(gui_treeview_selection());
|
||||
gtk_tree_selection_select_path(gui_treeview_selection(), path);
|
||||
__gui_treeview_set_sort_indicators();
|
||||
}
|
||||
|
||||
g_signal_stop_emission_by_name(treeview, "drag_data_received");
|
||||
gtk_drag_finish(context, true, true, time);
|
||||
gtk_tree_path_free(path);
|
||||
}
|
||||
|
||||
bool __gui_treeview_drag_drop(GtkTreeView *treeview, GdkDragContext *context,
|
||||
gint x, gint y, guint time, gpointer user_data)
|
||||
{
|
||||
gtk_drag_get_data(GTK_WIDGET(treeview), context,
|
||||
gdk_atom_intern(GUI_DRAG_DATA, false), time);
|
||||
return true;
|
||||
}
|
||||
|
||||
void gui_treeview_init()
|
||||
{
|
||||
GtkTreeViewColumn *col;
|
||||
int i, pos;
|
||||
|
||||
gtk_tree_view_set_model(gui_treeview(),
|
||||
GTK_TREE_MODEL(gui_filter_get()));
|
||||
gtk_tree_view_enable_model_drag_source(gui_treeview(), GDK_BUTTON1_MASK,
|
||||
gui_model_drag_targets, gui_model_n_targets,
|
||||
GDK_ACTION_MOVE);
|
||||
gtk_tree_view_enable_model_drag_dest(gui_treeview(),
|
||||
gui_model_drag_targets, gui_model_n_targets,
|
||||
GDK_ACTION_MOVE);
|
||||
|
||||
for (i = 0; i < GUI_MODEL_N_COLUMNS; i++) {
|
||||
col = gtk_tree_view_get_column(gui_treeview(), i);
|
||||
if (col) {
|
||||
g_signal_connect(col, "clicked",
|
||||
G_CALLBACK(__gui_treeview_column_clicked),
|
||||
GUINT_TO_POINTER(GUI_COL_MAP[i].compare));
|
||||
g_signal_connect(col, "notify::width",
|
||||
G_CALLBACK(__gui_treeview_column_resized),
|
||||
GUINT_TO_POINTER(i));
|
||||
pos = settings_get(GUI_COL_MAP[i].setting);
|
||||
if (pos > 0)
|
||||
gtk_tree_view_column_set_fixed_width(col, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gui_treeview_deinit()
|
||||
{
|
||||
__gui_treeview_clear_sorting();
|
||||
}
|
||||
|
||||
void gui_treeview_set_playlist(struct playlist *playlist)
|
||||
{
|
||||
gui_filter_set_playlist(playlist);
|
||||
|
||||
__gui_treeview_clear_sorting();
|
||||
__gui_treeview_set_sort_indicators();
|
||||
|
||||
gui_treeview_scroll();
|
||||
}
|
||||
|
||||
void gui_treeview_scroll()
|
||||
{
|
||||
int pos = playlist_current_index(gui_model_get_playlist());
|
||||
GtkTreePath *path;
|
||||
|
||||
if (!can_scroll || pos < 0)
|
||||
return;
|
||||
|
||||
path = gui_filter_path_from_index(pos);
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
gtk_tree_view_set_cursor(gui_treeview(), path, NULL, false);
|
||||
gtk_tree_view_scroll_to_cell(gui_treeview(), path, NULL, true, 0.5, 0.5);
|
||||
gtk_tree_path_free(path);
|
||||
}
|
||||
|
||||
void gui_treeview_select_path_at_pos(unsigned int x, unsigned int y)
|
||||
{
|
||||
GtkTreePath *path;
|
||||
|
||||
if (gtk_tree_view_get_path_at_pos(gui_treeview(), x, y,
|
||||
&path, NULL, NULL, NULL))
|
||||
{
|
||||
gtk_tree_selection_select_path(gui_treeview_selection(), path);
|
||||
gtk_tree_path_free(path);
|
||||
}
|
||||
}
|
||||
|
||||
GList *gui_treeview_list_selected_tracks(void)
|
||||
{
|
||||
GtkTreeSelection *selection = gui_treeview_selection();
|
||||
GList *rows = gtk_tree_selection_get_selected_rows(selection, NULL);
|
||||
GList *cur = g_list_first(rows);
|
||||
GList *list = NULL;
|
||||
|
||||
while (cur) {
|
||||
list = g_list_append(list, gui_filter_path_get_track(cur->data));
|
||||
cur = g_list_next(cur);
|
||||
}
|
||||
|
||||
g_list_free_full(rows, (GDestroyNotify) gtk_tree_path_free);
|
||||
gtk_tree_selection_unselect_all(selection);
|
||||
return list;
|
||||
}
|
451
gui/view.c
451
gui/view.c
|
@ -1,451 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/audio.h>
|
||||
#include <core/playlist.h>
|
||||
#include <core/settings.h>
|
||||
#include <gui/builder.h>
|
||||
#include <gui/model.h>
|
||||
#include <gui/queue.h>
|
||||
#include <gui/view.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static const gchar *QUEUE_SETTINGS[Q_MODEL_N_COLUMNS] = {
|
||||
[Q_MODEL_TRACK_NR] = "gui.queue.track",
|
||||
[Q_MODEL_TITLE] = "gui.queue.title",
|
||||
[Q_MODEL_LENGTH] = "gui.queue.length",
|
||||
[Q_MODEL_ARTIST] = "gui.queue.artist",
|
||||
[Q_MODEL_ALBUM] = "gui.queue.album",
|
||||
[Q_MODEL_YEAR] = "gui.queue.year",
|
||||
[Q_MODEL_GENRE] = "gui.queue.genre",
|
||||
[Q_MODEL_COUNT] = "gui.queue.count",
|
||||
[Q_MODEL_LAST_PLAY] = NULL,
|
||||
[Q_MODEL_FILE_PATH] = NULL,
|
||||
[Q_MODEL_FONT] = NULL,
|
||||
};
|
||||
|
||||
static const enum compare_t QUEUE_SORT[Q_MODEL_N_COLUMNS] = {
|
||||
[Q_MODEL_TRACK_NR] = COMPARE_TRACK,
|
||||
[Q_MODEL_TITLE] = COMPARE_TITLE,
|
||||
[Q_MODEL_LENGTH] = COMPARE_LENGTH,
|
||||
[Q_MODEL_ARTIST] = COMPARE_ARTIST,
|
||||
[Q_MODEL_ALBUM] = COMPARE_ALBUM,
|
||||
[Q_MODEL_YEAR] = COMPARE_YEAR,
|
||||
[Q_MODEL_GENRE] = COMPARE_GENRE,
|
||||
[Q_MODEL_COUNT] = COMPARE_COUNT,
|
||||
[Q_MODEL_LAST_PLAY] = COMPARE_PLAYED,
|
||||
};
|
||||
|
||||
static GtkTreeView *view_treeview = NULL;
|
||||
static GtkTreeModelFilter *view_filter = NULL;
|
||||
static unsigned int view_sort_count = 0;
|
||||
static bool view_no_scroll = false;
|
||||
|
||||
static inline GtkTreePath *__view_filter_convert_path(GtkTreePath *orig)
|
||||
{
|
||||
return gtk_tree_model_filter_convert_path_to_child_path(view_filter, orig);
|
||||
}
|
||||
|
||||
static struct track *__view_filter_get_track(GtkTreePath *orig)
|
||||
{
|
||||
GtkTreePath *real = __view_filter_convert_path(orig);
|
||||
struct track *track = gui_queue_model_path_get_track(real);
|
||||
|
||||
gtk_tree_path_free(real);
|
||||
return track;
|
||||
}
|
||||
|
||||
static unsigned int __view_filter_get_index(GtkTreePath *orig)
|
||||
{
|
||||
GtkTreePath *real = __view_filter_convert_path(orig);
|
||||
unsigned int ret = gtk_tree_path_get_indices(real)[0];
|
||||
|
||||
gtk_tree_path_free(real);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int __view_get_column_index(GtkTreeViewColumn *col)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < Q_MODEL_N_COLUMNS; i++) {
|
||||
if (col == gtk_tree_view_get_column(view_treeview, i))
|
||||
return i;
|
||||
}
|
||||
|
||||
return Q_MODEL_N_COLUMNS;
|
||||
}
|
||||
|
||||
static inline void __view_display_sorting(gchar *text)
|
||||
{
|
||||
gtk_label_set_text(GTK_LABEL(gui_builder_widget("o_sorting")), text);
|
||||
}
|
||||
|
||||
static int __view_dec_sort(gpointer data)
|
||||
{
|
||||
if (view_sort_count > 0)
|
||||
view_sort_count--;
|
||||
if (view_sort_count == 0)
|
||||
__view_display_sorting("");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void __view_set_column_sort_indicator(GtkTreeViewColumn *col,
|
||||
unsigned int index)
|
||||
{
|
||||
struct queue *queue = gui_queue_model_get_queue();
|
||||
GSList *cur = queue ? queue->q_sort : NULL;
|
||||
unsigned int order = GTK_SORT_ASCENDING;
|
||||
bool show = false;
|
||||
int field;
|
||||
|
||||
while (cur) {
|
||||
order = GTK_SORT_ASCENDING;
|
||||
field = GPOINTER_TO_INT(cur->data);
|
||||
|
||||
if (abs(field) == QUEUE_SORT[index]) {
|
||||
show = true;
|
||||
if (field < 0)
|
||||
order = GTK_SORT_DESCENDING;
|
||||
break;
|
||||
}
|
||||
|
||||
cur = g_slist_next(cur);
|
||||
}
|
||||
|
||||
gtk_tree_view_column_set_sort_indicator(col, show);
|
||||
gtk_tree_view_column_set_sort_order(col, order);
|
||||
}
|
||||
|
||||
static void __view_set_sort_indicators()
|
||||
{
|
||||
GtkTreeViewColumn *col;
|
||||
|
||||
for (unsigned int i = 0; i < Q_MODEL_N_COLUMNS; i++) {
|
||||
col = gtk_tree_view_get_column(view_treeview, i);
|
||||
if (col)
|
||||
__view_set_column_sort_indicator(col, i);
|
||||
}
|
||||
}
|
||||
|
||||
void __view_row_activated(GtkTreeView *treeview, GtkTreePath *path,
|
||||
GtkTreeViewColumn *col, gpointer data)
|
||||
{
|
||||
view_no_scroll = true;
|
||||
audio_load(__view_filter_get_track(path));
|
||||
queue_selected(gui_queue_model_get_queue(),
|
||||
gtk_tree_path_get_indices(path)[0]);
|
||||
view_no_scroll = false;
|
||||
}
|
||||
|
||||
void __view_column_resized(GtkTreeViewColumn *col, GParamSpec *pspec,
|
||||
gpointer data)
|
||||
{
|
||||
unsigned int index = __view_get_column_index(col);
|
||||
|
||||
settings_set(QUEUE_SETTINGS[index], gtk_tree_view_column_get_width(col));
|
||||
}
|
||||
|
||||
void __view_column_clicked(GtkTreeViewColumn *col, gpointer data)
|
||||
{
|
||||
struct queue *queue = gui_queue_model_get_queue();
|
||||
unsigned int index = __view_get_column_index(col);
|
||||
bool reset = view_sort_count == 0;
|
||||
gchar *text;
|
||||
|
||||
if (!queue || queue_has_flag(queue, Q_NO_SORT))
|
||||
return;
|
||||
|
||||
if (playlist_get_queue(PL_SYSTEM, gui_queue(queue)->gq_text))
|
||||
playlist_sort(PL_SYSTEM, gui_queue(queue)->gq_text, QUEUE_SORT[index], reset);
|
||||
else
|
||||
queue_sort(queue, QUEUE_SORT[index], reset);
|
||||
|
||||
if (view_sort_count == 0) {
|
||||
text = g_strdup_printf("Sorting within %s",
|
||||
gtk_tree_view_column_get_title(col));
|
||||
__view_display_sorting(text);
|
||||
g_free(text);
|
||||
}
|
||||
|
||||
__view_set_sort_indicators();
|
||||
|
||||
view_sort_count++;
|
||||
g_timeout_add_seconds(3, __view_dec_sort, NULL);
|
||||
}
|
||||
|
||||
struct view_add_data {
|
||||
enum playlist_type_t vad_type;
|
||||
const gchar *vad_name;
|
||||
};
|
||||
|
||||
static void __view_add_to_playlist(GtkTreeModel *model, GtkTreePath *path,
|
||||
GtkTreeIter *iter, gpointer data)
|
||||
{
|
||||
struct view_add_data *vad = (struct view_add_data *)data;
|
||||
playlist_add(vad->vad_type, vad->vad_name, __view_filter_get_track(path));
|
||||
}
|
||||
|
||||
static void __view_delete_selection(GtkTreeSelection *selection)
|
||||
{
|
||||
struct queue *queue = gui_queue_model_get_queue();
|
||||
GList *rows = gtk_tree_selection_get_selected_rows(selection, NULL);
|
||||
GList *cur = g_list_reverse(rows);
|
||||
|
||||
while (cur) {
|
||||
queue_erase(queue, __view_filter_get_index(cur->data));
|
||||
cur = g_list_next(cur);
|
||||
}
|
||||
|
||||
g_list_free_full(rows, (GDestroyNotify) gtk_tree_path_free);
|
||||
}
|
||||
|
||||
static gchar *__view_get_new_playlist_name(void)
|
||||
{
|
||||
gchar *text = NULL;
|
||||
GtkWidget *entry = gtk_entry_new();
|
||||
GtkWidget *dialog = gtk_dialog_new_with_buttons("New Playlist Name?",
|
||||
GTK_WINDOW(gui_builder_widget("o_window")),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
_("_OK"), GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
|
||||
|
||||
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
|
||||
gtk_entry_set_activates_default(GTK_ENTRY(entry), true);
|
||||
gtk_container_add(GTK_CONTAINER(content), entry);
|
||||
gtk_widget_show_all(dialog);
|
||||
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
|
||||
text = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
|
||||
gtk_widget_destroy(dialog);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
static void __view_process_selection(GtkTreeView *treeview, unsigned int keyval)
|
||||
{
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
|
||||
struct view_add_data vad_data;
|
||||
gchar *text = NULL;
|
||||
|
||||
switch (keyval) {
|
||||
case GDK_KEY_f:
|
||||
vad_data.vad_type = PL_SYSTEM;
|
||||
vad_data.vad_name = "Favorites";
|
||||
gtk_tree_selection_selected_foreach(selection,
|
||||
__view_add_to_playlist,
|
||||
&vad_data);
|
||||
break;
|
||||
case GDK_KEY_p:
|
||||
text = __view_get_new_playlist_name();
|
||||
if (!text || !playlist_new(PL_USER, text))
|
||||
break;
|
||||
vad_data.vad_type = PL_USER;
|
||||
vad_data.vad_name = text;
|
||||
gtk_tree_selection_selected_foreach(selection,
|
||||
__view_add_to_playlist,
|
||||
&vad_data);
|
||||
g_free(text);
|
||||
break;
|
||||
case GDK_KEY_q:
|
||||
vad_data.vad_type = PL_SYSTEM;
|
||||
vad_data.vad_name = "Queued Tracks";
|
||||
gtk_tree_selection_selected_foreach(selection,
|
||||
__view_add_to_playlist,
|
||||
&vad_data);
|
||||
break;
|
||||
case GDK_KEY_Delete:
|
||||
__view_delete_selection(selection);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void __view_keypress(GtkTreeView *treeview, GdkEventKey *event, gpointer data)
|
||||
{
|
||||
__view_process_selection(treeview, event->keyval);
|
||||
}
|
||||
|
||||
void __view_rc_new_queue(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
__view_process_selection(view_treeview, GDK_KEY_q);
|
||||
}
|
||||
|
||||
void __view_rc_add_to_queue(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
unsigned int i;
|
||||
gchar *name;
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
name = g_strdup_printf("o_queue_%d", i);
|
||||
if (GTK_WIDGET(item) == gui_builder_widget(name))
|
||||
__view_process_selection(view_treeview, GDK_KEY_0 + i);
|
||||
g_free(name);
|
||||
}
|
||||
}
|
||||
|
||||
void __view_rc_add_favorites(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
__view_process_selection(view_treeview, GDK_KEY_f);
|
||||
}
|
||||
|
||||
void __view_rc_add_hidden(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection(view_treeview);
|
||||
GList *rows = gtk_tree_selection_get_selected_rows(selection, NULL);
|
||||
GList *cur = g_list_reverse(rows);
|
||||
|
||||
while (cur) {
|
||||
playlist_add(PL_SYSTEM, "Hidden", __view_filter_get_track(cur->data));
|
||||
cur = g_list_next(cur);
|
||||
}
|
||||
|
||||
g_list_free_full(rows, (GDestroyNotify) gtk_tree_path_free);
|
||||
|
||||
}
|
||||
|
||||
void __view_rc_add_new(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
__view_process_selection(view_treeview, GDK_KEY_p);
|
||||
}
|
||||
|
||||
static void __view_rc_add_other(GtkWidget *item, gpointer data)
|
||||
{
|
||||
GtkTreeView *treeview = GTK_TREE_VIEW(gui_builder_widget("o_treeview"));
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
|
||||
struct playlist *playlist = (struct playlist *)data;
|
||||
struct view_add_data vad_data = {
|
||||
.vad_type = PL_USER,
|
||||
.vad_name = playlist->pl_name,
|
||||
};
|
||||
|
||||
gtk_tree_selection_selected_foreach(selection,
|
||||
__view_add_to_playlist,
|
||||
&vad_data);
|
||||
}
|
||||
|
||||
static gint __view_cmp_pl(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
struct playlist *pl_a = (struct playlist *)a;
|
||||
struct playlist *pl_b = (struct playlist *)b;
|
||||
return g_utf8_collate(pl_a->pl_name, pl_b->pl_name);
|
||||
}
|
||||
|
||||
static GtkWidget *__view_rc_build_submenu(void)
|
||||
{
|
||||
struct db_entry *dbe, *next;
|
||||
struct playlist *playlist;
|
||||
GtkWidget *submenu, *item;
|
||||
GList *iter, *list = NULL;
|
||||
|
||||
if (pl_user_db_get()->db_size == 0)
|
||||
return NULL;
|
||||
|
||||
db_for_each(dbe, next, pl_user_db_get()) {
|
||||
playlist = &USER_PLAYLIST(dbe)->pl_playlist;
|
||||
list = g_list_insert_sorted(list, playlist, __view_cmp_pl);
|
||||
}
|
||||
|
||||
submenu = gtk_menu_new();
|
||||
iter = g_list_first(list);
|
||||
while (iter) {
|
||||
playlist = (struct playlist *)iter->data;
|
||||
item = gtk_menu_item_new_with_label(playlist->pl_name);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
||||
g_signal_connect(item, "activate",
|
||||
G_CALLBACK(__view_rc_add_other), playlist);
|
||||
iter = g_list_next(iter);
|
||||
}
|
||||
|
||||
return submenu;
|
||||
}
|
||||
|
||||
bool __view_button_press(GtkTreeView *treeview, GdkEventButton *event,
|
||||
gpointer data)
|
||||
{
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
|
||||
GtkMenu *menu = GTK_MENU(gui_builder_widget("o_menu"));
|
||||
GtkWidget *submenu = NULL;
|
||||
GtkMenuItem *other;
|
||||
GtkTreePath *path;
|
||||
|
||||
if (event->button != GDK_BUTTON_SECONDARY)
|
||||
return false;
|
||||
|
||||
/* Select path if it isn't already selected */
|
||||
if (gtk_tree_view_get_path_at_pos(treeview, event->x, event->y,
|
||||
&path, NULL, NULL, NULL))
|
||||
{
|
||||
gtk_tree_selection_select_path(selection, path);
|
||||
gtk_tree_path_free(path);
|
||||
}
|
||||
|
||||
/* Set the "Other Playlists" submenu. */
|
||||
other = GTK_MENU_ITEM(gui_builder_widget("o_add_to_other"));
|
||||
submenu = __view_rc_build_submenu();
|
||||
gtk_menu_item_set_submenu(other, submenu);
|
||||
|
||||
if (!submenu)
|
||||
gtk_widget_hide(GTK_WIDGET(other));
|
||||
else
|
||||
gtk_widget_show_all(GTK_WIDGET(other));
|
||||
|
||||
gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time);
|
||||
return true;
|
||||
}
|
||||
|
||||
void gui_view_init()
|
||||
{
|
||||
GtkTreeViewColumn *col;
|
||||
int i, pos;
|
||||
|
||||
view_treeview = GTK_TREE_VIEW(gui_builder_widget("o_treeview"));
|
||||
view_filter = GTK_TREE_MODEL_FILTER(gtk_tree_model_filter_new(
|
||||
GTK_TREE_MODEL(gui_queue_model_get()), NULL));
|
||||
|
||||
for (i = 0; i < Q_MODEL_N_COLUMNS; i++) {
|
||||
col = gtk_tree_view_get_column(view_treeview, i);
|
||||
pos = settings_get(QUEUE_SETTINGS[i]);
|
||||
if (col && pos > 0)
|
||||
gtk_tree_view_column_set_fixed_width(col, pos);
|
||||
}
|
||||
|
||||
gtk_tree_view_set_model(view_treeview, GTK_TREE_MODEL(view_filter));
|
||||
}
|
||||
|
||||
GtkTreeModelFilter *gui_view_get_filter(void)
|
||||
{
|
||||
return view_filter;
|
||||
}
|
||||
|
||||
void gui_view_set_queue(struct queue *queue)
|
||||
{
|
||||
gui_queue_model_set_queue(queue);
|
||||
|
||||
view_sort_count = 0;
|
||||
__view_display_sorting("");
|
||||
__view_set_sort_indicators();
|
||||
|
||||
gui_view_scroll();
|
||||
}
|
||||
|
||||
void gui_view_scroll()
|
||||
{
|
||||
struct queue *queue = gui_queue_model_get_queue();
|
||||
GtkTreePath *real, *path;
|
||||
|
||||
if (!queue || (int)queue->q_cur.it_pos < 0 || view_no_scroll)
|
||||
return;
|
||||
|
||||
real = gtk_tree_path_new_from_indices(queue->q_cur.it_pos, -1);
|
||||
path = gtk_tree_model_filter_convert_child_path_to_path(view_filter, real);
|
||||
if (!path)
|
||||
goto out;
|
||||
|
||||
gtk_tree_view_set_cursor(view_treeview, path, NULL, false);
|
||||
gtk_tree_view_scroll_to_cell(view_treeview, path, NULL, TRUE, 0.5, 0.5);
|
||||
gtk_tree_path_free(path);
|
||||
out:
|
||||
gtk_tree_path_free(real);
|
||||
}
|
36
gui/window.c
36
gui/window.c
|
@ -3,13 +3,17 @@
|
|||
*/
|
||||
#include <core/settings.h>
|
||||
#include <core/version.h>
|
||||
#include <gui/builder.h>
|
||||
#include <gui/window.h>
|
||||
|
||||
static const gchar *SETTINGS_WIDTH = "gui.window.width";
|
||||
static const gchar *SETTINGS_HEIGHT = "gui.window.height";
|
||||
static const gchar *SETTINGS_X = "gui.window.x";
|
||||
static const gchar *SETTINGS_Y = "gui.window.y";
|
||||
|
||||
static int saved_width = 0;
|
||||
static int saved_height = 0;
|
||||
static int saved_x = 0;
|
||||
static int saved_y = 0;
|
||||
|
||||
|
||||
gboolean __window_configure(GtkWindow *window, GdkEventConfigure *event,
|
||||
|
@ -17,6 +21,8 @@ gboolean __window_configure(GtkWindow *window, GdkEventConfigure *event,
|
|||
{
|
||||
int width = settings_get(SETTINGS_WIDTH);
|
||||
int height = settings_get(SETTINGS_HEIGHT);
|
||||
int x = settings_get(SETTINGS_X);
|
||||
int y = settings_get(SETTINGS_Y);
|
||||
|
||||
if (event->width != width) {
|
||||
saved_width = width;
|
||||
|
@ -28,6 +34,16 @@ gboolean __window_configure(GtkWindow *window, GdkEventConfigure *event,
|
|||
settings_set(SETTINGS_HEIGHT, event->height);
|
||||
}
|
||||
|
||||
if (event->x != x) {
|
||||
saved_x = x;
|
||||
settings_set(SETTINGS_X, event->x);
|
||||
}
|
||||
|
||||
if (event->y != y) {
|
||||
saved_y = y;
|
||||
settings_set(SETTINGS_Y, event->y);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -38,6 +54,8 @@ gboolean __window_state(GtkWindow *window, GdkEventWindowState *event,
|
|||
event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
|
||||
settings_set(SETTINGS_WIDTH, saved_width);
|
||||
settings_set(SETTINGS_HEIGHT, saved_height);
|
||||
settings_set(SETTINGS_X, saved_x);
|
||||
settings_set(SETTINGS_Y, saved_y);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
@ -46,21 +64,25 @@ gboolean __window_state(GtkWindow *window, GdkEventWindowState *event,
|
|||
|
||||
void gui_window_init(const gchar *icon)
|
||||
{
|
||||
GtkWindow *window = GTK_WINDOW(gui_builder_widget("o_window"));
|
||||
gchar *title = g_strdup_printf("Ocarina %s", get_version());
|
||||
|
||||
gtk_window_set_title(window, title);
|
||||
gtk_window_set_icon_from_file(window, icon, NULL);
|
||||
gtk_window_set_title(gui_window(), title);
|
||||
gtk_window_set_icon_from_file(gui_window(), icon, NULL);
|
||||
|
||||
saved_width = settings_get(SETTINGS_WIDTH);
|
||||
saved_height = settings_get(SETTINGS_HEIGHT);
|
||||
if (saved_width > 0 && saved_height > 0)
|
||||
gtk_window_resize(window, saved_width, saved_height);
|
||||
saved_x = settings_get(SETTINGS_X);
|
||||
saved_y = settings_get(SETTINGS_Y);
|
||||
|
||||
if (saved_width > 0 || saved_height > 0)
|
||||
gtk_window_resize(gui_window(), saved_width, saved_height);
|
||||
if (saved_x > 0 || saved_y > 0)
|
||||
gtk_window_move(gui_window(), saved_x, saved_y);
|
||||
|
||||
g_free(title);
|
||||
}
|
||||
|
||||
void gui_window_deinit()
|
||||
{
|
||||
gtk_widget_destroy(gui_builder_widget("o_window"));
|
||||
gtk_widget_destroy(GTK_WIDGET(gui_window()));
|
||||
}
|
||||
|
|
|
@ -11,20 +11,20 @@
|
|||
#include <gst/gst.h>
|
||||
|
||||
|
||||
struct audio_ops {
|
||||
struct audio_callbacks {
|
||||
/* Called when a track is loaded. */
|
||||
void (*on_load)(struct track *);
|
||||
void (*audio_cb_load)(struct track *);
|
||||
|
||||
/* Called when playback state changes. */
|
||||
void (*on_state_change)(GstState);
|
||||
void (*audio_cb_state_change)(GstState);
|
||||
|
||||
/* Called when the automatic pause state changes. */
|
||||
void (*on_config_pause)(int);
|
||||
void (*audio_cb_config_pause)(int);
|
||||
};
|
||||
|
||||
|
||||
/* Called to initialize the audio manager. */
|
||||
void audio_init(int *, char ***, struct audio_ops *);
|
||||
void audio_init(int *, char ***, struct audio_callbacks *);
|
||||
|
||||
/* Called to deinitialize the audio manager. */
|
||||
void audio_deinit();
|
||||
|
@ -33,8 +33,9 @@ void audio_deinit();
|
|||
void audio_save();
|
||||
|
||||
|
||||
/* Called to load a track for playback. */
|
||||
/* Called to load either a track or file for playback. */
|
||||
bool audio_load(struct track *);
|
||||
bool audio_load_filepath(const gchar *);
|
||||
|
||||
/* Called to get the current track. */
|
||||
struct track *audio_cur_track();
|
||||
|
@ -72,16 +73,16 @@ struct track *audio_next();
|
|||
/* Called to load the previous track. */
|
||||
struct track *audio_prev();
|
||||
|
||||
/* Called when playback has reached the end-of-stream position. */
|
||||
struct track *audio_eos();
|
||||
|
||||
/* Called when gstreamer has received an error. */
|
||||
void audio_error(GstMessage *);
|
||||
|
||||
/* Called to configure automatic pausing. */
|
||||
void audio_pause_after(int);
|
||||
/*
|
||||
* Called to configure automatic pausing.
|
||||
* Returns true if the value has been changed.
|
||||
*/
|
||||
bool audio_pause_after(int);
|
||||
int audio_get_pause_count(void);
|
||||
|
||||
#ifdef CONFIG_TESTING
|
||||
GstElement *test_audio_player();
|
||||
void test_audio_eos();
|
||||
void test_audio_error(GError *, gchar *);
|
||||
GstElement *test_audio_pipeline();
|
||||
#endif /* CONFIG_TESTING */
|
||||
#endif /* OCARINA_CORE_AUDIO_H */
|
||||
|
|
|
@ -3,17 +3,16 @@
|
|||
*/
|
||||
#ifndef OCARINA_CORE_CORE_H
|
||||
#define OCARINA_CORE_CORE_H
|
||||
#include <core/queue.h>
|
||||
|
||||
|
||||
struct core_init_data {
|
||||
struct queue_ops *playlist_ops;
|
||||
struct audio_ops *audio_ops;
|
||||
};
|
||||
|
||||
#include <core/audio.h>
|
||||
#include <core/idle.h>
|
||||
#include <core/playlist.h>
|
||||
#include <core/settings.h>
|
||||
#include <core/tags/tags.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Called to initialize all core Ocarina components. */
|
||||
void core_init(int *, char ***, struct core_init_data *);
|
||||
void core_init(int *, char ***, struct playlist_callbacks *,
|
||||
struct audio_callbacks *, enum idle_sync_t);
|
||||
|
||||
/* Called to deinitialize all core Ocarina componentns. */
|
||||
void core_deinit();
|
||||
|
|
|
@ -48,7 +48,7 @@ static inline void *DBE_DATA(struct db_entry *dbe)
|
|||
|
||||
struct db_ops {
|
||||
/* Allocate a new struct db_entry from a given key. */
|
||||
struct db_entry *(*dbe_alloc)(const gchar *);
|
||||
struct db_entry *(*dbe_alloc)(const gchar *, unsigned int);
|
||||
|
||||
/* Free a struct db_entry. */
|
||||
void (*dbe_free)(struct db_entry *);
|
||||
|
@ -57,7 +57,7 @@ struct db_ops {
|
|||
gchar *(*dbe_key)(struct db_entry *);
|
||||
|
||||
/* Read a single struct db_entry from disk. */
|
||||
struct db_entry *(*dbe_read)(struct file *);
|
||||
struct db_entry *(*dbe_read)(struct file *, unsigned int);
|
||||
|
||||
/* Write a single struct db_entry to disk. */
|
||||
void (*dbe_write)(struct file *, struct db_entry *);
|
||||
|
@ -78,7 +78,7 @@ struct database {
|
|||
{ \
|
||||
.db_size = 0, \
|
||||
.db_autosave = autosave, \
|
||||
.db_file = FILE_INIT(fname, fmin), \
|
||||
.db_file = FILE_INIT_DATA("", fname, fmin), \
|
||||
.db_entries = g_ptr_array_new(), \
|
||||
.db_keys = g_hash_table_new(g_str_hash, g_str_equal), \
|
||||
.db_ops = ops, \
|
||||
|
|
|
@ -30,105 +30,100 @@
|
|||
|
||||
|
||||
enum open_mode {
|
||||
OPEN_READ,
|
||||
OPEN_WRITE,
|
||||
CLOSED, /* File is not open. */
|
||||
OPEN_READ, /* File is open for reading text. */
|
||||
OPEN_READ_BINARY, /* File is open for reading binary data. */
|
||||
OPEN_WRITE, /* File is open for writing text. */
|
||||
OPEN_WRITE_BINARY, /* File is open for writing binary data. */
|
||||
};
|
||||
|
||||
struct file {
|
||||
FILE *f_file; /* The file's IO stream. */
|
||||
const gchar *f_name; /* The file's basename. */
|
||||
const gchar *f_subdir; /* The file's subdirectory. */
|
||||
|
||||
enum open_mode f_mode; /* The file's current open mode. */
|
||||
unsigned int f_version; /* The file's current data version. */
|
||||
unsigned int f_prev; /* The file's on-disk data version. */
|
||||
unsigned int f_min; /* The file's minimum data version. */
|
||||
FILE *f_file; /* The file's IO stream. */
|
||||
const gchar *f_name; /* The file's basename. */
|
||||
|
||||
const gchar *(*f_user_dir)(void); /* The file's user directory. */
|
||||
};
|
||||
|
||||
#define FILE_INIT(fname, min) \
|
||||
{ \
|
||||
.f_mode = OPEN_READ, \
|
||||
.f_version = OCARINA_MINOR_VERSION, \
|
||||
.f_prev = 0, \
|
||||
.f_min = min, \
|
||||
.f_file = NULL, \
|
||||
.f_name = fname, \
|
||||
#define FILE_INIT_DATA(fdir, fname, min) \
|
||||
{ \
|
||||
.f_file = NULL, \
|
||||
.f_name = fname, \
|
||||
.f_subdir = fdir, \
|
||||
.f_mode = CLOSED, \
|
||||
.f_version = OCARINA_MINOR_VERSION, \
|
||||
.f_prev = 0, \
|
||||
.f_min = min, \
|
||||
.f_user_dir = g_get_user_data_dir, \
|
||||
}
|
||||
|
||||
|
||||
struct cache_file {
|
||||
FILE *cf_file; /* The cache file's IO stream. */
|
||||
gchar *cf_name; /* The cache file's basename. */
|
||||
gchar *cf_subdir; /* The cache file's subdirectory. */
|
||||
};
|
||||
|
||||
#define CACHE_FILE_INIT(cfsubdir, cfname) \
|
||||
{ \
|
||||
.cf_file = NULL, \
|
||||
.cf_subdir = cfsubdir, \
|
||||
.cf_name = cfname, \
|
||||
}
|
||||
|
||||
/* Initialize a new file object. */
|
||||
void file_init(struct file *, const gchar *, unsigned int);
|
||||
void cache_file_init(struct cache_file *, gchar *, gchar *);
|
||||
/* Initialize a file object. */
|
||||
void file_init_data(struct file *, const gchar *, const gchar *, unsigned int);
|
||||
void file_init_cache(struct file *, const gchar *, const gchar *);
|
||||
|
||||
/*
|
||||
* Returns the full path of the file or an empty string if filename is not set.
|
||||
* These functions allocates a new string that MUST be freed with g_free().
|
||||
* NOTE: This function allocates a new string that MUST be freed with g_free().
|
||||
*/
|
||||
gchar *file_path(struct file *);
|
||||
gchar *cache_file_path(struct cache_file *);
|
||||
|
||||
/*
|
||||
* Returns the path to the temporary file used for writes.
|
||||
* This function allocates a new string that MUST be freed with g_free().
|
||||
* NOTE: This function allocates a new string that MUST be freed with g_free().
|
||||
*/
|
||||
gchar *file_write_path(struct file *);
|
||||
gchar *cache_file_write_path(struct cache_file *);
|
||||
|
||||
/* Returns the version number of the file. */
|
||||
const unsigned int file_version(struct file *);
|
||||
|
||||
/* Returns true if the file exists on disk and false otherwise. */
|
||||
bool file_exists(struct file *);
|
||||
bool cache_file_exists(struct cache_file *);
|
||||
|
||||
/*
|
||||
* Call to open a file for either reading or writing. Callers
|
||||
* are expected to call file_close() when IO is completed.
|
||||
*
|
||||
* When opening a file for reading (OPEN_READ):
|
||||
* - Check if the file exists.
|
||||
* - Read in file->_prev_version from the start of the file.
|
||||
* When opening a file for reading (OPEN_READ / OPEN_READ_BINARY):
|
||||
* - Check if the file exists
|
||||
* - If open for reading text (OPEN_READ):
|
||||
* - Read in file->_prev_version from the start of the file.
|
||||
*
|
||||
* When opening a file for writing (OPEN_WRITE):
|
||||
* When opening a file for writing (OPEN_WRITE / OPEN_WRITE_BINARY):
|
||||
* - Create missing directories as needed.
|
||||
* - Open a temporary file to protect data if Ocarina crashes.
|
||||
* - Write file->_version to the start of the file (data files only).
|
||||
* - If open for writing text (OPEN_WRITE):
|
||||
* - Write file->_version to the start of the file (data files only).
|
||||
*
|
||||
* Returns true if the open was successful and false otherwise.
|
||||
* Oening a cache file with OPEN_READ is currently unsupported.
|
||||
*/
|
||||
bool file_open(struct file *, enum open_mode);
|
||||
bool cache_file_open(struct cache_file *, enum open_mode);
|
||||
|
||||
/*
|
||||
* Closes an open file, setting file->{f|cf}_file to NULL. If the file was opened
|
||||
* with OPEN_WRITE, then rename the temporary file to file_path().
|
||||
* Closes an open file, setting file->f_file to NULL and file->f_mode
|
||||
* to CLOSED. If the file was opened for writing, then rename the
|
||||
* temporary file to file_path().
|
||||
*/
|
||||
void file_close(struct file *);
|
||||
void cache_file_close(struct cache_file *);
|
||||
|
||||
/*
|
||||
* Read an entire line from the file and return it to the caller.
|
||||
* This function allocates a new string that MUST be freed with g_free().
|
||||
* Called to read an unsigned int, signed int, single word, or entire
|
||||
* line from the file.
|
||||
* NOTE: file_readw() and file_readl() both return a new string that
|
||||
* MUST be freed with g_free()
|
||||
*/
|
||||
gchar *file_readw(struct file *);
|
||||
gchar *file_readl(struct file *);
|
||||
|
||||
/*
|
||||
* Read from a file with an fscanf(3) style format string.
|
||||
* Returns the number of items matched.
|
||||
*/
|
||||
int file_readf(struct file *, const char *, ...);
|
||||
unsigned int file_readu(struct file *);
|
||||
static inline int file_readd(struct file *file)
|
||||
{ return (int)file_readu(file); }
|
||||
static inline unsigned short int file_readhu(struct file *file)
|
||||
{ return (unsigned short int)file_readu(file); }
|
||||
|
||||
/*
|
||||
* Write to a file with an fprintf(3) style format string.
|
||||
|
@ -137,13 +132,19 @@ int file_readf(struct file *, const char *, ...);
|
|||
int file_writef(struct file *, const char *, ...);
|
||||
|
||||
/*
|
||||
* Write binary data to a cache file similar to fwrite(3).
|
||||
* Reads the contents of a file as binary data.
|
||||
* NOTE: This function returns a new string which MUST be freed with g_free().
|
||||
*/
|
||||
gchar *file_read(struct file *);
|
||||
|
||||
/*
|
||||
* Write binary data a cache file, similar to fwrite(3).
|
||||
* Returns the number of bytes successfully written.
|
||||
*/
|
||||
int cache_file_write(struct cache_file *, const void *, size_t);
|
||||
int file_write(struct file *, const void *, size_t);
|
||||
|
||||
/* Import a file into the cache. */
|
||||
bool cache_file_import(struct cache_file *, const gchar *);
|
||||
bool file_import(struct file *, const gchar *);
|
||||
|
||||
/* Removes a closed file from disk. */
|
||||
bool file_remove(struct file *);
|
||||
|
|
|
@ -24,10 +24,7 @@ enum idle_sync_t {
|
|||
|
||||
|
||||
/* Called to initialize the idle queue. */
|
||||
void idle_init();
|
||||
#ifdef CONFIG_TESTING
|
||||
void idle_init_sync();
|
||||
#endif /* CONFIG_TESTING */
|
||||
void idle_init(enum idle_sync_t);
|
||||
|
||||
/* Called to deinitialize the idle queue. */
|
||||
void idle_deinit();
|
||||
|
|
|
@ -7,16 +7,15 @@
|
|||
*/
|
||||
#ifndef OCARINA_CORE_PLAYLIST_H
|
||||
#define OCARINA_CORE_PLAYLIST_H
|
||||
|
||||
#include <core/playlists/artist.h>
|
||||
#include <core/playlists/generic.h>
|
||||
#include <core/playlists/library.h>
|
||||
#include <core/playlists/system.h>
|
||||
#include <core/playlists/user.h>
|
||||
#include <core/queue.h>
|
||||
|
||||
|
||||
/* Called to initialize the playlist manager. */
|
||||
void playlist_init(struct queue_ops *);
|
||||
void playlist_init(struct playlist_callbacks *);
|
||||
|
||||
/* Called to deinitialize the playlist manager. */
|
||||
void playlist_deinit();
|
||||
|
@ -24,43 +23,30 @@ void playlist_deinit();
|
|||
/* Called to force-save all playlists. */
|
||||
void playlist_save();
|
||||
|
||||
/* Called to select the current playlist. */
|
||||
bool playlist_select(enum playlist_type_t, const gchar *);
|
||||
/* Called to notify all playlists that a track has been played. */
|
||||
void playlist_played(struct track *);
|
||||
|
||||
/* Called to notify all playlists that a track has been selected. */
|
||||
void playlist_selected(struct track *);
|
||||
|
||||
|
||||
/* Called to create a new playlist. */
|
||||
bool playlist_new(enum playlist_type_t, const gchar *);
|
||||
struct playlist *playlist_new(enum playlist_type_t, const gchar *);
|
||||
|
||||
/* Called to delete a playlist. */
|
||||
bool playlist_delete(enum playlist_type_t, const gchar *);
|
||||
bool playlist_delete(struct playlist *);
|
||||
|
||||
|
||||
/* Called to add a track to a playlist. */
|
||||
bool playlist_add(enum playlist_type_t, const gchar *, struct track *);
|
||||
/* Called to look up playlists either by name or id. */
|
||||
struct playlist *playlist_lookup(enum playlist_type_t, const gchar *);
|
||||
struct playlist *playlist_get(enum playlist_type_t, unsigned int);
|
||||
|
||||
/* Called to remove a track from a playlist. */
|
||||
bool playlist_remove(enum playlist_type_t, const gchar *, struct track *);
|
||||
|
||||
/* Called to update tracks on a playlist. */
|
||||
void playlist_update(enum playlist_type_t, const gchar *);
|
||||
/* Called to access the current playlist. */
|
||||
struct playlist *playlist_current(void);
|
||||
|
||||
|
||||
/* Called to check if a specific track is in the playlist. */
|
||||
bool playlist_has(enum playlist_type_t, const gchar *, struct track *);
|
||||
|
||||
/* Called to find the number of tracks in the playlist. */
|
||||
unsigned int playlist_size(enum playlist_type_t, const gchar *);
|
||||
|
||||
|
||||
/* Called to set the playlist's random flag. */
|
||||
void playlist_set_random(enum playlist_type_t, const gchar *, bool);
|
||||
|
||||
/* Called to check the playlist's random flag. */
|
||||
bool playlist_get_random(enum playlist_type_t, const gchar *);
|
||||
|
||||
/* Called to change the sort order of the playlist. */
|
||||
void playlist_sort(enum playlist_type_t, const gchar *, enum compare_t, bool);
|
||||
|
||||
/* Called to select the current playlist. */
|
||||
bool playlist_select(struct playlist *);
|
||||
|
||||
/* Called to get the next track from the default playlist. */
|
||||
struct track *playlist_next(void);
|
||||
|
@ -69,13 +55,27 @@ struct track *playlist_next(void);
|
|||
struct track *playlist_prev(void);
|
||||
|
||||
|
||||
/* Called to access the playlist queue. */
|
||||
struct queue *playlist_get_queue(enum playlist_type_t, const gchar *);
|
||||
/* Called to add a track to a playlist. */
|
||||
bool playlist_add(struct playlist *, struct track *);
|
||||
|
||||
/* Called to convert a playlist name to an integer id. */
|
||||
unsigned int playlist_get_id(enum playlist_type_t, const gchar *);
|
||||
/* Called to remove a track from a playlist. */
|
||||
bool playlist_remove(struct playlist *, struct track *);
|
||||
|
||||
/* Called to convert a playlist id to a name. */
|
||||
gchar *playlist_get_name(enum playlist_type_t, unsigned int);
|
||||
/* Called to check if a specific track is in the playlist. */
|
||||
bool playlist_has(struct playlist *, struct track *);
|
||||
|
||||
|
||||
/* Called to set the playlist's random flag. */
|
||||
void playlist_set_random(struct playlist *, bool);
|
||||
|
||||
/* Called to change the sort order of the playlist. */
|
||||
bool playlist_sort(struct playlist *, enum compare_t);
|
||||
|
||||
/* Called to manually rearrange the order of the playlist. */
|
||||
bool playlist_rearrange(struct playlist *, unsigned int, unsigned int);
|
||||
|
||||
|
||||
/* Called to set the playlist's search text */
|
||||
void playlist_set_search(struct playlist *, const gchar *);
|
||||
|
||||
#endif /* OCARINA_CORE_PLAYLIST_H */
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
*/
|
||||
#ifndef OCARINA_CORE_PLAYLISTS_ARTIST_H
|
||||
#define OCARINA_CORE_PLAYLISTS_ARTIST_H
|
||||
#include <core/playlists/type.h>
|
||||
#include <core/playlists/generic.h>
|
||||
|
||||
/* Artist playlist type. */
|
||||
extern struct playlist_type pl_artist;
|
||||
|
||||
|
||||
/* Called to initialize artist playlists. */
|
||||
void pl_artist_init(struct queue_ops *ops);
|
||||
void pl_artist_init(void);
|
||||
|
||||
/* Called to deinitialize library playlists. */
|
||||
void pl_artist_deinit();
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_CORE_PLAYLISTS_GENERIC_H
|
||||
#define OCARINA_CORE_PLAYLISTS_GENERIC_H
|
||||
#include <core/playlists/iterator.h>
|
||||
#include <core/playlists/playlist.h>
|
||||
|
||||
enum playlist_save_flags {
|
||||
PL_SAVE_FLAGS = (1 << 0), /* Save playlist random and sort data. */
|
||||
PL_SAVE_ITER = (1 << 1), /* Save playlist iterator position. */
|
||||
PL_SAVE_TRACKS = (1 << 2), /* Save playlist tracks. */
|
||||
};
|
||||
#define PL_SAVE_METADATA (PL_SAVE_FLAGS | PL_SAVE_ITER)
|
||||
#define PL_SAVE_ALL (PL_SAVE_TRACKS | PL_SAVE_METADATA)
|
||||
|
||||
struct playlist_callbacks {
|
||||
/* Called to notify that a new playlist has been allocated. */
|
||||
void (*pl_cb_alloc)(struct playlist *);
|
||||
|
||||
/* Called to notify that a track has been added. */
|
||||
void (*pl_cb_added)(struct playlist *, struct track *);
|
||||
|
||||
/*
|
||||
* Called to notify that N instances of a track have been removed.
|
||||
* Track may be NULL to indicate that several different tracks were
|
||||
* removed at once.
|
||||
*/
|
||||
void (*pl_cb_removed)(struct playlist *, struct track *, unsigned int n);
|
||||
|
||||
/*
|
||||
* Called to notify that a track has been updated.
|
||||
* If the track is NULL, then the entire playlist should be updated.
|
||||
*/
|
||||
void (*pl_cb_updated)(struct playlist *, struct track *);
|
||||
};
|
||||
|
||||
|
||||
/* Called to set playlist callbacks. */
|
||||
void playlist_generic_set_callbacks(struct playlist_callbacks *);
|
||||
|
||||
/* Generic playlist init functions. */
|
||||
void playlist_generic_init(struct playlist *, unsigned int, ...);
|
||||
|
||||
/* Generic playlist deinit function. */
|
||||
void playlist_generic_deinit(struct playlist *);
|
||||
|
||||
/* Generic playlist alloc function. */
|
||||
struct playlist *playlist_generic_alloc(gchar *, enum playlist_type_t,
|
||||
unsigned int, struct playlist_ops *,
|
||||
unsigned int, ...);
|
||||
|
||||
/* Generic playlist free function. */
|
||||
void playlist_generic_free(struct playlist *);
|
||||
|
||||
/* Generic playlist save function. */
|
||||
void playlist_generic_save(struct playlist *, struct file *, unsigned int);
|
||||
|
||||
/* Generic playlist load function. */
|
||||
void playlist_generic_load(struct playlist *, struct file *, unsigned int);
|
||||
|
||||
/* Generic playlist can-select function. */
|
||||
bool playlist_generic_can_select(struct playlist *);
|
||||
|
||||
/* Generic playlist clear operation. */
|
||||
void playlist_generic_clear(struct playlist *);
|
||||
|
||||
/* Generic playlist add track operations. */
|
||||
bool playlist_generic_add(struct playlist *, struct track *);
|
||||
bool playlist_generic_add_front(struct playlist *, struct track *);
|
||||
|
||||
/* Generic playlist remove track operation. */
|
||||
bool playlist_generic_remove(struct playlist *, struct track *);
|
||||
|
||||
/* Generic playlist update track operation. */
|
||||
void playlist_generic_update(struct playlist *, struct track *);
|
||||
|
||||
/* Generic playlist set_random operation. */
|
||||
void playlist_generic_set_random(struct playlist *, bool);
|
||||
|
||||
/* Generic playlist sorting operations. */
|
||||
void playlist_generic_sort(struct playlist *, enum compare_t);
|
||||
void playlist_generic_resort(struct playlist *);
|
||||
|
||||
/* Generic playlist rearranging operation. */
|
||||
bool playlist_generic_rearrange(struct playlist *, unsigned int, unsigned int);
|
||||
|
||||
/* Generic playlist next track operation. */
|
||||
struct track *playlist_generic_next(struct playlist *);
|
||||
|
||||
#endif /* OCARINA_CORE_PLAYLISTS_GENERIC_H */
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*
|
||||
* NOTE: The playlist_iter type is defined in include/core/playlists/playlist.h
|
||||
*/
|
||||
#ifndef OCARINA_CORE_PLAYLISTS_ITERATOR_H
|
||||
#define OCARINA_CORE_PLAYLISTS_ITERATOR_H
|
||||
#include <core/playlists/playlist.h>
|
||||
|
||||
/* Called to set the playlist iterator to a specific position. */
|
||||
static inline playlist_iter playlist_iter_get(struct playlist *playlist,
|
||||
unsigned int n)
|
||||
{
|
||||
return playlist ? g_queue_peek_nth_link(&playlist->pl_tracks, n) : NULL;
|
||||
}
|
||||
|
||||
/* Called to advance the requested playlist iterator. */
|
||||
static inline playlist_iter playlist_iter_next(playlist_iter iter)
|
||||
{
|
||||
return g_list_next(iter);
|
||||
}
|
||||
|
||||
/* Called to get a pointer to the track at the requested iterator. */
|
||||
static inline struct track *playlist_iter_track(playlist_iter iter)
|
||||
{
|
||||
return iter ? iter->data : NULL;
|
||||
}
|
||||
|
||||
/* Called to find the playlist index of the requested iterator. */
|
||||
static inline int playlist_iter_index(struct playlist *playlist,
|
||||
playlist_iter iter)
|
||||
{
|
||||
return (playlist && iter) ? g_queue_link_index(&playlist->pl_tracks, iter) : -1;
|
||||
}
|
||||
|
||||
/* Called to iterate over the entire playlist. */
|
||||
#define playlist_for_each(playlist, it) \
|
||||
for (it = playlist_iter_get(playlist, 0); it; it = playlist_iter_next(it))
|
||||
|
||||
|
||||
/* Called to set the index of the current track. */
|
||||
static inline bool playlist_current_set(struct playlist *playlist,
|
||||
unsigned int n)
|
||||
{
|
||||
if (playlist)
|
||||
playlist->pl_current = playlist_iter_get(playlist, n);
|
||||
return playlist && playlist->pl_current;
|
||||
}
|
||||
|
||||
/* Called to advance the current track. */
|
||||
static inline bool playlist_current_next(struct playlist *playlist)
|
||||
{
|
||||
if (playlist)
|
||||
playlist->pl_current = playlist_iter_next(playlist->pl_current);
|
||||
return playlist && playlist->pl_current;
|
||||
}
|
||||
|
||||
/* Called to rewind the current track. */
|
||||
static inline bool playlist_current_previous(struct playlist *playlist)
|
||||
{
|
||||
if (playlist)
|
||||
playlist->pl_current = g_list_previous(playlist->pl_current);
|
||||
return playlist && playlist->pl_current;
|
||||
}
|
||||
|
||||
/* Called to get a pointer to the current track. */
|
||||
static inline struct track *playlist_current_track(struct playlist *playlist)
|
||||
{
|
||||
return playlist ? playlist_iter_track(playlist->pl_current) : NULL;
|
||||
}
|
||||
|
||||
/* Called to get the playlist index of the current track. */
|
||||
static inline int playlist_current_index(struct playlist *playlist)
|
||||
{
|
||||
return playlist ? playlist_iter_index(playlist, playlist->pl_current) : -1;
|
||||
}
|
||||
|
||||
/* Called to find the nth track on a playlist. */
|
||||
static inline struct track *playlist_at(struct playlist *playlist, unsigned int n)
|
||||
{
|
||||
return playlist_iter_track(playlist_iter_get(playlist, n));
|
||||
}
|
||||
#endif /* OCARINA_CORE_PLAYLISTS_ITERATOR_H */
|
|
@ -3,17 +3,19 @@
|
|||
*/
|
||||
#ifndef OCARINA_CORE_PLAYLISTS_LIBRARY_H
|
||||
#define OCARINA_CORE_PLAYLISTS_LIBRARY_H
|
||||
#include <core/playlists/type.h>
|
||||
|
||||
#include <core/playlists/generic.h>
|
||||
|
||||
/* Library playlist type. */
|
||||
extern struct playlist_type pl_library;
|
||||
|
||||
|
||||
/* Called to initialize library playlists. */
|
||||
void pl_library_init(struct queue_ops *);
|
||||
void pl_library_init(void);
|
||||
|
||||
/* Called to deinitialize system playlists. */
|
||||
void pl_library_deinit();
|
||||
|
||||
/* Called to update a library path. */
|
||||
void pl_library_update(struct playlist *);
|
||||
|
||||
#endif /* OCARINA_CORE_PLAYLISTS_LIBRARY_H */
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_CORE_PLAYLISTS_PLAYLIST_H
|
||||
#define OCARINA_CORE_PLAYLISTS_PLAYLIST_H
|
||||
#include <core/tags/track.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef GList * playlist_iter;
|
||||
struct playlist;
|
||||
|
||||
enum playlist_type_t {
|
||||
PL_SYSTEM,
|
||||
PL_ARTIST,
|
||||
PL_LIBRARY,
|
||||
PL_USER,
|
||||
PL_MAX_TYPE,
|
||||
};
|
||||
|
||||
|
||||
#define PL_RANDOM (1 << 1)
|
||||
|
||||
|
||||
struct playlist_ops {
|
||||
/* Called to add a track to a playlist. */
|
||||
bool (*pl_add)(struct playlist *, struct track *);
|
||||
|
||||
/* Called to check if a playlist can be selected. */
|
||||
bool (*pl_can_select)(struct playlist *);
|
||||
|
||||
/* Called to delete a playlist. */
|
||||
bool (*pl_delete)(struct playlist *);
|
||||
|
||||
/* Called to remove a track from the playlist. */
|
||||
bool (*pl_remove)(struct playlist *, struct track *);
|
||||
|
||||
/* Called to set a playlist flag. */
|
||||
void (*pl_set_random)(struct playlist *, bool);
|
||||
|
||||
/* Called to sort the playlist. */
|
||||
void (*pl_sort)(struct playlist *, enum compare_t);
|
||||
|
||||
/* Called to rearrange the playlist. */
|
||||
bool (*pl_rearrange)(struct playlist *, unsigned int, unsigned int);
|
||||
};
|
||||
|
||||
|
||||
struct playlist {
|
||||
enum playlist_type_t pl_type; /* This playlist's type. */
|
||||
gchar *pl_name; /* This playlist's name. */
|
||||
unsigned int pl_id; /* This playlist's identifier. */
|
||||
|
||||
GQueue pl_tracks; /* This playlist's queue of tracks. */
|
||||
unsigned int pl_length; /* This playlist's length, in seconds. */
|
||||
bool pl_random; /* This playlist's random setting. */
|
||||
playlist_iter pl_current; /* This playlist's current track. */
|
||||
GSList *pl_sort; /* This playlist's sort order. */
|
||||
gchar **pl_search; /* This playlist's search text. */
|
||||
|
||||
const struct playlist_ops *pl_ops; /* This playlist's supported operations. */
|
||||
};
|
||||
|
||||
|
||||
#define DEFINE_PLAYLIST(type, name, id, ops) { \
|
||||
.pl_type = type, \
|
||||
.pl_name = name, \
|
||||
.pl_id = id, \
|
||||
.pl_ops = ops, \
|
||||
}
|
||||
|
||||
|
||||
struct playlist_type {
|
||||
/* Called to save all playlists of the given type. */
|
||||
void (*pl_save)(void);
|
||||
|
||||
/* Called to look up playlists. */
|
||||
struct playlist *(*pl_lookup)(const gchar *);
|
||||
struct playlist *(*pl_get)(unsigned int);
|
||||
|
||||
/* Called to create a new playlist. */
|
||||
struct playlist *(*pl_new)(const gchar *);
|
||||
|
||||
/* Called to notify that a track has been played. */
|
||||
void (*pl_played)(struct track *);
|
||||
|
||||
/* Called to notify that a track has been selected. */
|
||||
void (*pl_selected)(struct track *);
|
||||
};
|
||||
|
||||
|
||||
/* Called to check if the playlist contains a specific track. */
|
||||
static inline bool playlist_has(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
return playlist ? g_queue_find(&playlist->pl_tracks, track) != NULL : false;
|
||||
}
|
||||
|
||||
/* Called to find the size of a playlist. */
|
||||
static inline unsigned int playlist_size(struct playlist *playlist)
|
||||
{
|
||||
return playlist ? g_queue_get_length(&playlist->pl_tracks) : 0;
|
||||
}
|
||||
|
||||
/* Called to clear the sort order of the playlist. */
|
||||
static inline void playlist_clear_sort(struct playlist *playlist)
|
||||
{
|
||||
if (playlist) {
|
||||
g_slist_free(playlist->pl_sort);
|
||||
playlist->pl_sort = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OCARINA_CORE_PLAYLISTS_PLAYLIST_H */
|
|
@ -3,8 +3,7 @@
|
|||
*/
|
||||
#ifndef OCARINA_CORE_PLAYLISTS_SYSTEM_H
|
||||
#define OCARINA_CORE_PLAYLISTS_SYSTEM_H
|
||||
#include <core/playlists/type.h>
|
||||
|
||||
#include <core/playlists/generic.h>
|
||||
|
||||
enum sys_playlist_t {
|
||||
SYS_PL_FAVORITES, /* Songs that the user likes. */
|
||||
|
@ -18,29 +17,13 @@ enum sys_playlist_t {
|
|||
SYS_PL_NUM_PLAYLISTS, /* Number of system playlists. */
|
||||
};
|
||||
|
||||
struct sys_playlist {
|
||||
struct playlist spl_playlist;
|
||||
|
||||
void (*spl_init)(struct playlist *, unsigned int, struct queue_ops *);
|
||||
void (*spl_save)(struct playlist *, struct file *);
|
||||
void (*spl_load)(struct playlist *, struct file *);
|
||||
bool (*spl_can_select)(struct playlist *);
|
||||
bool (*spl_add)(struct playlist *, struct track *);
|
||||
bool (*spl_remove)(struct playlist *, struct track *);
|
||||
void (*spl_clear)(struct playlist *);
|
||||
bool (*spl_update)(struct playlist *, struct track *);
|
||||
void (*spl_set_flag)(struct playlist *, enum queue_flags, bool);
|
||||
void (*spl_sort)(struct playlist *, enum compare_t, bool);
|
||||
struct track *(*spl_next)(struct playlist *);
|
||||
};
|
||||
|
||||
|
||||
/* System playlist type. */
|
||||
extern struct playlist_type pl_system;
|
||||
|
||||
|
||||
/* Called to initialize system playlists. */
|
||||
void pl_system_init(struct queue_ops *);
|
||||
void pl_system_init(void);
|
||||
|
||||
/* Called to deinitialize system playlists. */
|
||||
void pl_system_deinit();
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_CORE_PLAYLISTS_TYPE_H
|
||||
#define OCARINA_CORE_PLAYLISTS_TYPE_H
|
||||
#include <core/queue.h>
|
||||
#include <core/tags/track.h>
|
||||
#include <glib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
enum playlist_type_t {
|
||||
PL_SYSTEM,
|
||||
PL_ARTIST,
|
||||
PL_LIBRARY,
|
||||
PL_USER,
|
||||
PL_MAX_TYPE,
|
||||
};
|
||||
|
||||
|
||||
struct playlist {
|
||||
enum playlist_type_t pl_type; /* This playlist's type. */
|
||||
gchar *pl_name; /* This playlist's name. */
|
||||
struct queue pl_queue; /* This playlist's queue of tracks. */
|
||||
};
|
||||
|
||||
|
||||
#define DEFINE_PLAYLIST(type, name) { .pl_type = type, .pl_name = name }
|
||||
|
||||
|
||||
struct playlist_type {
|
||||
/* Called to save all playlists of the given type. */
|
||||
void (*pl_save)(void);
|
||||
|
||||
/* Called to get the queue for the playlist. */
|
||||
struct queue *(*pl_get_queue)(const gchar *);
|
||||
|
||||
/* Called to convert a playlist name to an integer id. */
|
||||
unsigned int (*pl_get_id)(const gchar *);
|
||||
|
||||
/* Called to convert a playlist id to a name. */
|
||||
gchar *(*pl_get_name)(unsigned int);
|
||||
|
||||
/* Called to check if a playlist can be selected. */
|
||||
bool (*pl_can_select)(const gchar *);
|
||||
|
||||
/* Called to create a new playlist. */
|
||||
bool (*pl_new)(const gchar *);
|
||||
|
||||
/* Called to delete a playlist. */
|
||||
bool (*pl_delete)(const gchar *);
|
||||
|
||||
/* Called to add a track to the playlist. */
|
||||
bool (*pl_add_track)(const gchar *, struct track *);
|
||||
|
||||
/* Called to remove a track from the playlist. */
|
||||
bool (*pl_remove_track)(const gchar *, struct track *);
|
||||
|
||||
/* Called to update a playlist. */
|
||||
void (*pl_update)(const gchar *);
|
||||
|
||||
/* Called to set a playlist flag. */
|
||||
void (*pl_set_flag)(const gchar *, enum queue_flags, bool);
|
||||
|
||||
/* Called to sort a playlist. */
|
||||
void (*pl_sort)(const gchar *, enum compare_t, bool);
|
||||
|
||||
/* Called to pick the next track from a playlist. */
|
||||
struct track *(*pl_next)(const gchar *);
|
||||
};
|
||||
|
||||
|
||||
/* Noop playlist can-select operation. */
|
||||
bool playlist_noop_can_select(struct playlist *);
|
||||
|
||||
/* Noop playlist clear operation. */
|
||||
void playlist_noop_clear(struct playlist *);
|
||||
|
||||
/* Noop playlist set_flag operation. */
|
||||
void playlist_noop_set_flag(struct playlist *, enum queue_flags, bool);
|
||||
|
||||
/* Noop playlist sorting operation. */
|
||||
void playlist_noop_sort(struct playlist *, enum compare_t, bool);
|
||||
|
||||
|
||||
/* Generic playlist init function. */
|
||||
void playlist_generic_init(struct playlist *, unsigned int, struct queue_ops *);
|
||||
|
||||
/* Generic playlist can-select function. */
|
||||
bool playlist_generic_can_select(struct playlist *);
|
||||
|
||||
/* Generic playlist clear operation. */
|
||||
void playlist_generic_clear(struct playlist *);
|
||||
|
||||
/* Generic playlist add track operation. */
|
||||
bool playlist_generic_add_track(struct playlist *, struct track *);
|
||||
|
||||
/* Generic playlist remove track operation. */
|
||||
bool playlist_generic_remove_track(struct playlist *, struct track *);
|
||||
|
||||
/* Generic playlist update operation. */
|
||||
void playlist_generic_update(struct playlist *,
|
||||
bool (*)(struct playlist *, struct track *));
|
||||
|
||||
/* Generic playlist set_flag operation. */
|
||||
void playlist_generic_set_flag(struct playlist *, enum queue_flags, bool);
|
||||
|
||||
/* Generic playlist sorting operation. */
|
||||
void playlist_generic_sort(struct playlist *, enum compare_t, bool);
|
||||
|
||||
/* Generic playlist next track operation. */
|
||||
struct track *playlist_generic_next(struct playlist *);
|
||||
|
||||
|
||||
#endif /* OCARINA_CORE_PLAYLISTS_TYPE_H */
|
|
@ -3,8 +3,7 @@
|
|||
*/
|
||||
#ifndef OCARINA_CORE_PLAYLISTS_USER_H
|
||||
#define OCARINA_CORE_PLAYLISTS_USER_H
|
||||
#include <core/playlists/type.h>
|
||||
|
||||
#include <core/playlists/generic.h>
|
||||
|
||||
struct user_playlist {
|
||||
struct playlist pl_playlist;
|
||||
|
@ -19,10 +18,16 @@ extern struct playlist_type pl_user;
|
|||
|
||||
|
||||
/* Called to initialize user playlists. */
|
||||
void pl_user_init(struct queue_ops *ops);
|
||||
void pl_user_init(void);
|
||||
|
||||
/* Called to deinitialize user playlists. */
|
||||
void pl_user_deinit();
|
||||
|
||||
/* Called to tell user playlists that a track is getting deleted. */
|
||||
void pl_user_delete_track(struct track *);
|
||||
|
||||
/* Called to rename a user playlist. */
|
||||
bool pl_user_rename(struct playlist *, const gchar *);
|
||||
|
||||
struct database *pl_user_db_get();
|
||||
#endif /* OCARINA_CORE_PLAYLISTS_USER_H */
|
||||
|
|
|
@ -1,196 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*
|
||||
* Queues are lists of tracks that the user has requested to play next.
|
||||
* Users of queues are expected to implement their own save and load functions,
|
||||
* and to provide a filled out queue_ops structure during initialization.
|
||||
*/
|
||||
#ifndef OCARINA_CORE_QUEUE_H
|
||||
#define OCARINA_CORE_QUEUE_H
|
||||
|
||||
#include <core/file.h>
|
||||
#include <core/tags/track.h>
|
||||
|
||||
struct queue;
|
||||
|
||||
|
||||
enum queue_flags {
|
||||
Q_ENABLED = (1 << 0), /* Queue is enabled. */
|
||||
Q_RANDOM = (1 << 1), /* Queue will pick songs randomly. */
|
||||
Q_REPEAT = (1 << 2), /* Queue will not remove songs when picked. */
|
||||
Q_NO_SORT = (1 << 3), /* Queue will not be sorted. */
|
||||
Q_SAVE_FLAGS = (1 << 4), /* Queue will be saved when flags change. */
|
||||
Q_SAVE_SORT = (1 << 5), /* Queue will be saved when sorted. */
|
||||
Q_ADD_FRONT = (1 << 6), /* Queue will add new tracks at the front. */
|
||||
};
|
||||
|
||||
|
||||
struct queue_ops {
|
||||
/* Called to tell a higher layer that a queue has been initialized. */
|
||||
void *(*qop_init)(struct queue *, void *);
|
||||
|
||||
/* Called to tell a higher layer that a queue is deinitializing. */
|
||||
void (*qop_deinit)(struct queue *);
|
||||
|
||||
/* Called to tell a higher layer that a track has been added. */
|
||||
void (*qop_added)(struct queue *, unsigned int);
|
||||
|
||||
/* Called to ask a higher layer if a track can be erased. */
|
||||
bool (*qop_erase)(struct queue *, struct track *);
|
||||
|
||||
/* Called to tell a higher layer that a track has been removed. */
|
||||
void (*qop_removed)(struct queue *, unsigned int);
|
||||
|
||||
/* Called to tell a higher layer that the queue has been cleared. */
|
||||
void (*qop_cleared)(struct queue *, unsigned int);
|
||||
|
||||
/* Called to have a higher layer save the queue. */
|
||||
void (*qop_save)(struct queue *, enum queue_flags);
|
||||
|
||||
/* Called to tell a higher layer that a track has been updated. */
|
||||
void (*qop_updated)(struct queue *, unsigned int);
|
||||
};
|
||||
|
||||
|
||||
struct queue_iter {
|
||||
GList *it_iter; /* The current link in the GQueue. */
|
||||
guint it_pos; /* The current index into the queue. */
|
||||
};
|
||||
|
||||
|
||||
struct queue {
|
||||
unsigned int q_flags; /* The queue's set of flags. */
|
||||
unsigned int q_length; /* The queue's total runtime (in seconds). */
|
||||
GQueue q_tracks; /* The queue's list of tracks. */
|
||||
GSList *q_sort; /* The queue's sort order. */
|
||||
void *q_private; /* The queue's private data. */
|
||||
|
||||
struct queue_iter q_cur; /* The queue's last-played position. */
|
||||
const struct queue_ops *q_ops; /* The queue's operations vector. */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Called to initialize a queue iterator. */
|
||||
static inline void queue_iter_init(struct queue *queue, struct queue_iter *it)
|
||||
{
|
||||
it->it_iter = g_queue_peek_head_link(&queue->q_tracks);
|
||||
it->it_pos = g_queue_link_index(&queue->q_tracks, it->it_iter);
|
||||
}
|
||||
|
||||
/* Called to advance a queue iterator by one step. */
|
||||
static inline void queue_iter_next(struct queue_iter *it)
|
||||
{
|
||||
it->it_iter = g_list_next(it->it_iter);
|
||||
it->it_pos++;
|
||||
}
|
||||
|
||||
/* Called to rewind a queue iterator by one step. */
|
||||
static inline void queue_iter_prev(struct queue_iter *it)
|
||||
{
|
||||
it->it_iter = g_list_previous(it->it_iter);
|
||||
it->it_pos--;
|
||||
}
|
||||
|
||||
/* Called to set a queue iterator to a specific position. */
|
||||
static inline void queue_iter_set(struct queue *queue, struct queue_iter *it,
|
||||
unsigned int pos)
|
||||
{
|
||||
it->it_iter = g_queue_peek_nth_link(&queue->q_tracks, pos);
|
||||
it->it_pos = pos;
|
||||
}
|
||||
|
||||
/* Called to access the value of a queue iterator. */
|
||||
static inline struct track *queue_iter_val(struct queue_iter *it)
|
||||
{
|
||||
return (it->it_iter) ? it->it_iter->data : NULL;
|
||||
}
|
||||
|
||||
#define queue_for_each(queue, it) \
|
||||
for (queue_iter_init(queue, it); (it)->it_iter; queue_iter_next(it))
|
||||
|
||||
|
||||
|
||||
/* Called to initialize a queue. */
|
||||
void queue_init(struct queue *, unsigned int, const struct queue_ops *, void *);
|
||||
|
||||
/* Called to deinitialize a queue. */
|
||||
void queue_deinit(struct queue *);
|
||||
|
||||
|
||||
/* Called to save queue flags, sort order, and (optionally) iterator pos. */
|
||||
void queue_save_flags(struct queue *, struct file *, bool);
|
||||
|
||||
/* Called to save the list of queued tracks. */
|
||||
void queue_save_tracks(struct queue *, struct file *);
|
||||
|
||||
/* Called to load flags, sort order, and (optionally) iterator pos from file. */
|
||||
void queue_load_flags(struct queue *, struct file *, bool);
|
||||
|
||||
/* Called to load queued tracks from file. */
|
||||
void queue_load_tracks(struct queue *, struct file *);
|
||||
|
||||
|
||||
/* Called to set a queue flag. */
|
||||
void queue_set_flag(struct queue *, enum queue_flags);
|
||||
|
||||
/* Called to clear a queue flag. */
|
||||
void queue_unset_flag(struct queue *, enum queue_flags);
|
||||
|
||||
/* Called to check if the queue has a specific flag set. */
|
||||
static inline bool queue_has_flag(struct queue *queue, enum queue_flags flag)
|
||||
{
|
||||
return (queue->q_flags & flag) == (unsigned int)flag;
|
||||
}
|
||||
|
||||
/* Called to find the size of the queue. */
|
||||
static inline unsigned int queue_size(struct queue *queue)
|
||||
{
|
||||
return g_queue_get_length(&queue->q_tracks);
|
||||
}
|
||||
|
||||
/* Called to access the queued track at a given index. */
|
||||
static inline struct track *queue_at(struct queue *queue, unsigned int index)
|
||||
{
|
||||
return (struct track *)g_queue_peek_nth(&queue->q_tracks, index);
|
||||
}
|
||||
|
||||
|
||||
/* Called to add a track to the queue. */
|
||||
unsigned int queue_add(struct queue *, struct track *);
|
||||
|
||||
/*
|
||||
* Called to erase a track from the queue by index.
|
||||
* This can be prevented if qop_erase() returns "false".
|
||||
*/
|
||||
void queue_erase(struct queue *, unsigned int);
|
||||
|
||||
/* Called to remove a track from the queue by index. */
|
||||
void queue_remove(struct queue *, unsigned int);
|
||||
|
||||
/* Called to remove all instances of the track from the queue. */
|
||||
unsigned int queue_remove_all(struct queue *, struct track *);
|
||||
|
||||
/* Called to remove all tracks from the queue. */
|
||||
void queue_clear(struct queue *);
|
||||
|
||||
/* Called to check if a queue has a track. */
|
||||
bool queue_has(struct queue *, struct track *);
|
||||
|
||||
/* Called to tell the queue that a track has been updated. */
|
||||
void queue_updated(struct queue *, struct track *);
|
||||
|
||||
|
||||
/* Called to tell the queue that a specific index has been selected. */
|
||||
struct track *queue_selected(struct queue *, unsigned int);
|
||||
|
||||
/* Called to pick the next track from the queue. */
|
||||
struct track *queue_next(struct queue *);
|
||||
|
||||
/* Called to sort the queue without changing sort order. */
|
||||
void queue_resort(struct queue *);
|
||||
|
||||
/* Called to change the sort order and resort the queue. */
|
||||
void queue_sort(struct queue *, enum compare_t, bool);
|
||||
|
||||
#endif /* OCARINA_CORE_QUEUE_H */
|
|
@ -39,6 +39,9 @@ static inline bool string_match(const gchar *a, const gchar *b)
|
|||
/* Returns True if one of the tokens begins with the specified prefix. */
|
||||
bool string_match_token(const gchar *, gchar **);
|
||||
|
||||
/* Returns True if string a is a subdirectory of string b. */
|
||||
bool string_is_subdir(const gchar *, const gchar *);
|
||||
|
||||
/* Return the length of the string, with NULL checks */
|
||||
static inline int string_length(const gchar *str)
|
||||
{
|
||||
|
|
|
@ -37,9 +37,11 @@ bool library_db_defrag();
|
|||
const struct database *library_db_get();
|
||||
|
||||
/*
|
||||
* Called to find a library tag by library path. The difference is that
|
||||
* Called to find a library tag by path. The difference is that
|
||||
* library_find() will allocate a new library struct if the requested one
|
||||
* doesn't exist yet, but library_lookup() will return NULL in this situation.
|
||||
*
|
||||
* Note that path may be a subdirectory of the returned library.
|
||||
*/
|
||||
struct library *library_find(const gchar *);
|
||||
struct library *library_lookup(const gchar *);
|
||||
|
|
|
@ -58,6 +58,7 @@ struct track {
|
|||
};
|
||||
|
||||
#define TRACK(dbe) ((struct track *)DBE_DATA(dbe))
|
||||
#define TRACK_IS_EXTERNAL(track) (track->tr_library == NULL)
|
||||
|
||||
|
||||
/* Called to initialize the track database. */
|
||||
|
@ -87,6 +88,12 @@ unsigned int track_db_count_plays();
|
|||
/* Called to find the average play count of tracks in the database. */
|
||||
unsigned int track_db_average_plays();
|
||||
|
||||
/* Called to allocate a track without adding it to the database. */
|
||||
struct track *track_alloc_external(const gchar *);
|
||||
|
||||
/* Called to free an external track. */
|
||||
void track_free_external(struct track *);
|
||||
|
||||
/* Called to add a track tag to the database. */
|
||||
struct track *track_add(struct library *, const gchar *);
|
||||
|
||||
|
@ -99,6 +106,9 @@ void track_remove_all(struct library *);
|
|||
/* Called to get a track tag with a specific index. */
|
||||
struct track *track_get(const unsigned int);
|
||||
|
||||
/* Called to look up a track tag using only a filepath. */
|
||||
struct track *track_lookup(const gchar *);
|
||||
|
||||
/* Called to compare two track tags */
|
||||
int track_compare(struct track *, struct track *, enum compare_t);
|
||||
|
||||
|
|
|
@ -3,8 +3,19 @@
|
|||
*/
|
||||
#ifndef OCARINA_GUI_ARTWORK_H
|
||||
#define OCARINA_GUI_ARTWORK_H
|
||||
#include <core/tags/track.h>
|
||||
#include <gui/builder.h>
|
||||
|
||||
/* Called to set artwork for a track. */
|
||||
void gui_artwork_set_cover(void);
|
||||
|
||||
/* Called to import artwork for a track. */
|
||||
void gui_artwork_import(struct track *, gchar *);
|
||||
|
||||
/* Called to get the cover image. */
|
||||
static inline GtkImage *gui_artwork(void)
|
||||
{
|
||||
return GTK_IMAGE(gui_builder_widget("artwork"));
|
||||
}
|
||||
|
||||
#endif /* OCARINA_GUI_ARTWORK_H */
|
||||
|
|
|
@ -3,14 +3,107 @@
|
|||
*/
|
||||
#ifndef OCARINA_GUI_AUDIO_H
|
||||
#define OCARINA_GUI_AUDIO_H
|
||||
#include <core/audio.h>
|
||||
#include <gui/builder.h>
|
||||
|
||||
/* Audio callback functions. */
|
||||
extern struct audio_ops audio_ops;
|
||||
extern struct audio_callbacks audio_cb;
|
||||
|
||||
/* Called to initialize the GUI audio controls. */
|
||||
void gui_audio_init();
|
||||
|
||||
#ifdef CONFIG_TESTING
|
||||
void test_gui_audio_timeout();
|
||||
#endif /* CONFIG_TESTING */
|
||||
/* Called to stop the GUI audio timeout function. */
|
||||
void gui_audio_deinit();
|
||||
|
||||
/* Called to update the current track position. */
|
||||
int gui_audio_timeout();
|
||||
int gui_audio_popover_timeout();
|
||||
|
||||
/* Called to get the label displaying the album tag. */
|
||||
static inline GtkLabel *gui_album_tag(void)
|
||||
{
|
||||
return GTK_LABEL(gui_builder_widget("album_tag"));
|
||||
}
|
||||
|
||||
/* Called to get the label displaying the artist tag. */
|
||||
static inline GtkLabel *gui_artist_tag(void)
|
||||
{
|
||||
return GTK_LABEL(gui_builder_widget("artist_tag"));
|
||||
}
|
||||
|
||||
/* Called to get the label displaying the title tag. */
|
||||
static inline GtkLabel *gui_title_tag(void)
|
||||
{
|
||||
return GTK_LABEL(gui_builder_widget("title_tag"));
|
||||
}
|
||||
|
||||
/* Called to get the label displaying the track's position. */
|
||||
static inline GtkLabel *gui_position(void)
|
||||
{
|
||||
return GTK_LABEL(gui_builder_widget("position"));
|
||||
}
|
||||
|
||||
/* Called to get the label displaying the track's duration. */
|
||||
static inline GtkLabel *gui_duration(void)
|
||||
{
|
||||
return GTK_LABEL(gui_builder_widget("duration"));
|
||||
}
|
||||
|
||||
/* Called to get the play button. */
|
||||
static inline GtkButton *gui_play_button(void)
|
||||
{
|
||||
return GTK_BUTTON(gui_builder_widget("play_button"));
|
||||
}
|
||||
|
||||
/* Called to get the pause button. */
|
||||
static inline GtkButton *gui_pause_button(void)
|
||||
{
|
||||
return GTK_BUTTON(gui_builder_widget("pause_button"));
|
||||
}
|
||||
|
||||
/* Called to get the previous button. */
|
||||
static inline GtkButton *gui_prev_button(void)
|
||||
{
|
||||
return GTK_BUTTON(gui_builder_widget("prev_button"));
|
||||
}
|
||||
|
||||
/* Called to get the next button. */
|
||||
static inline GtkButton *gui_next_button(void)
|
||||
{
|
||||
return GTK_BUTTON(gui_builder_widget("next_button"));
|
||||
}
|
||||
|
||||
/* Called to get the pause-after widgets. */
|
||||
static inline GtkEntry *gui_pause_entry(void)
|
||||
{
|
||||
return GTK_ENTRY(gui_builder_widget("pause_entry"));
|
||||
}
|
||||
|
||||
static inline GtkButton *gui_pause_down(void)
|
||||
{
|
||||
return GTK_BUTTON(gui_builder_widget("pause_down"));
|
||||
}
|
||||
|
||||
static inline GtkButton *gui_pause_up(void)
|
||||
{
|
||||
return GTK_BUTTON(gui_builder_widget("pause_up"));
|
||||
}
|
||||
|
||||
static inline GtkPopover *gui_pause_popover(void)
|
||||
{
|
||||
return GTK_POPOVER(gui_builder_widget("pause_popover"));
|
||||
}
|
||||
|
||||
/* Called to get the seeking GtkAdjustment. */
|
||||
static inline GtkAdjustment *gui_seek(void)
|
||||
{
|
||||
return GTK_ADJUSTMENT(gui_builder_object("seek"));
|
||||
}
|
||||
|
||||
/* Called to get the volume button. */
|
||||
static inline GtkScaleButton *gui_volume_button(void)
|
||||
{
|
||||
return GTK_SCALE_BUTTON(gui_builder_widget("volume_button"));
|
||||
}
|
||||
|
||||
#endif /* OCARINA_GUI_AUDIO_H */
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_GUI_COLLECTION_H
|
||||
#define OCARINA_GUI_COLLECTION_H
|
||||
|
||||
/* Called to initialize the GUI collection code. */
|
||||
void gui_collection_init();
|
||||
|
||||
#endif /* OCARINA_GUI_COLLECTION_H */
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_GUI_FILTER_H
|
||||
#define OCARINA_GUI_FILTER_H
|
||||
#include <core/playlist.h>
|
||||
#include <gui/builder.h>
|
||||
|
||||
enum gui_filter_how {
|
||||
GUI_FILTER_DEFAULT,
|
||||
GUI_FILTER_ALBUM,
|
||||
GUI_FILTER_ARTIST,
|
||||
GUI_FILTER_GENRE,
|
||||
GUI_FILTER_TITLE,
|
||||
};
|
||||
|
||||
/* Called to initialize the filter model. */
|
||||
void gui_filter_init();
|
||||
|
||||
/* Called to deinitialize the filter model. */
|
||||
void gui_filter_deinit();
|
||||
|
||||
/* Called to set the current playlist. */
|
||||
void gui_filter_set_playlist(struct playlist *);
|
||||
|
||||
/* Called to get the filter model. */
|
||||
GtkTreeModelFilter *gui_filter_get();
|
||||
|
||||
/* Called to convert a filter model path into a track. */
|
||||
struct track *gui_filter_path_get_track(GtkTreePath *);
|
||||
|
||||
/* Called to load the track at path. */
|
||||
void gui_filter_path_load_track(GtkTreePath *);
|
||||
|
||||
/* Called to convert a filter model path into a queue index. */
|
||||
unsigned int gui_filter_path_get_index(GtkTreePath *);
|
||||
|
||||
/* Called to convert a playlist iterator index into a path. */
|
||||
GtkTreePath *gui_filter_path_from_index(unsigned int);
|
||||
|
||||
/* Called to refilter a playlist. Pass NULL to refilter the current playlist */
|
||||
void gui_filter_refilter(struct playlist *);
|
||||
|
||||
/* Called to access the filter search-entry. */
|
||||
static inline GtkSearchEntry *gui_filter_search(void)
|
||||
{
|
||||
return GTK_SEARCH_ENTRY(gui_builder_widget("filter_search"));
|
||||
}
|
||||
|
||||
/* Called to access the filter-how chooser. */
|
||||
static inline GtkComboBox *gui_filter_how(void)
|
||||
{
|
||||
return GTK_COMBO_BOX(gui_builder_widget("filter_how"));
|
||||
}
|
||||
|
||||
#endif /* OCARINA_GUI_FILTER_H */
|
|
@ -3,6 +3,8 @@
|
|||
*/
|
||||
#ifndef OCARINA_GUI_IDLE_H
|
||||
#define OCARINA_GUI_IDLE_H
|
||||
#include <core/idle.h>
|
||||
#include <gui/builder.h>
|
||||
|
||||
/* Called to enable processing idle queue tasks. */
|
||||
void gui_idle_enable();
|
||||
|
@ -10,4 +12,10 @@ void gui_idle_enable();
|
|||
/* Called to disable processing idle queue tasks. */
|
||||
void gui_idle_disable();
|
||||
|
||||
/* Called to get a pointer to the idle progress bar. */
|
||||
static inline GtkProgressBar *gui_progress_bar()
|
||||
{
|
||||
return GTK_PROGRESS_BAR(gui_builder_widget("progress_bar"));
|
||||
}
|
||||
|
||||
#endif /* OCARINA_GUI_IDLE_H */
|
||||
|
|
|
@ -3,86 +3,97 @@
|
|||
*/
|
||||
#ifndef OCARINA_GUI_MODEL_H
|
||||
#define OCARINA_GUI_MODEL_H
|
||||
#include <core/queue.h>
|
||||
#include <core/playlist.h>
|
||||
#include <gui/builder.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define GUI_QUEUE_MODEL_TYPE (gui_queue_model_get_type())
|
||||
#define GUI_QUEUE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
|
||||
GUI_QUEUE_MODEL_TYPE, GuiQueueModel))
|
||||
#define GUI_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
|
||||
gui_model_get_type(), GuiModel))
|
||||
|
||||
enum queue_model_columns {
|
||||
Q_MODEL_TRACK_NR,
|
||||
Q_MODEL_TITLE,
|
||||
Q_MODEL_LENGTH,
|
||||
Q_MODEL_ARTIST,
|
||||
Q_MODEL_ALBUM,
|
||||
Q_MODEL_YEAR,
|
||||
Q_MODEL_GENRE,
|
||||
Q_MODEL_COUNT,
|
||||
Q_MODEL_LAST_PLAY,
|
||||
Q_MODEL_FILE_PATH,
|
||||
Q_MODEL_FONT,
|
||||
Q_MODEL_N_COLUMNS,
|
||||
enum gui_model_columns {
|
||||
GUI_MODEL_TRACK_NR,
|
||||
GUI_MODEL_TITLE,
|
||||
GUI_MODEL_LENGTH,
|
||||
GUI_MODEL_ARTIST,
|
||||
GUI_MODEL_ALBUM,
|
||||
GUI_MODEL_YEAR,
|
||||
GUI_MODEL_GENRE,
|
||||
GUI_MODEL_COUNT,
|
||||
GUI_MODEL_LAST_PLAY,
|
||||
GUI_MODEL_FILE_PATH,
|
||||
GUI_MODEL_FONT,
|
||||
GUI_MODEL_N_COLUMNS,
|
||||
};
|
||||
|
||||
|
||||
typedef struct _gui_queue_model GuiQueueModel;
|
||||
typedef struct _gui_queue_model_class GuiQueueModelClass;
|
||||
|
||||
struct _gui_queue_model {
|
||||
GObject gqm_parent; /* This MUST be the first member. */
|
||||
gint gqm_stamp; /* This is used to check iter validity. */
|
||||
|
||||
struct queue *gqm_queue; /* The model's associated queue. */
|
||||
struct queue_iter gqm_iter; /* The current _q_iter. */
|
||||
struct gui_model {
|
||||
GObject gm_parent; /* This MUST be the first member. */
|
||||
gint gm_stamp; /* This is used to check iter validity. */
|
||||
};
|
||||
typedef struct gui_model GuiModel;
|
||||
|
||||
struct _gui_queue_model_class {
|
||||
|
||||
struct gui_model_class {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
typedef struct gui_model_class GuiModelClass;
|
||||
|
||||
struct gui_model_drag_data {
|
||||
unsigned int drag_row;
|
||||
struct track *drag_track;
|
||||
};
|
||||
|
||||
/* Called to initialize the GuiQueueModel */
|
||||
void gui_queue_model_init(void);
|
||||
#define GUI_DRAG_DATA "GUI_DRAG_DATA"
|
||||
|
||||
/* Called to deinitialize the GuiQueueModel */
|
||||
void gui_queue_model_deinit(void);
|
||||
extern const GtkTargetEntry gui_model_drag_targets[];
|
||||
extern const unsigned int gui_model_n_targets;
|
||||
|
||||
/* Called to get the GuiQueueModel */
|
||||
GuiQueueModel *gui_queue_model_get(void);
|
||||
/* Called to initialize the GuiModel */
|
||||
void gui_model_init(void);
|
||||
|
||||
/* Called to find the GType of the GuiQueueModel */
|
||||
GType gui_queue_model_get_type();
|
||||
/* Called to deinitialize the GuiModel */
|
||||
void gui_model_deinit(void);
|
||||
|
||||
/* Called to get the GuiModel */
|
||||
GuiModel *gui_model_get(void);
|
||||
|
||||
/* Called to find the GType of the GuiModel */
|
||||
GType gui_model_get_type();
|
||||
|
||||
/* Called to add a row to the model */
|
||||
void gui_queue_model_add(struct queue *, unsigned int);
|
||||
void gui_model_add(struct playlist *, struct track *);
|
||||
|
||||
/* Called to remove a row from the model */
|
||||
void gui_queue_model_remove(struct queue *, unsigned int);
|
||||
void gui_model_remove(struct playlist *, struct track *, unsigned int);
|
||||
|
||||
/* Called to remove all rows from the model */
|
||||
void gui_queue_model_clear(struct queue *, unsigned int);
|
||||
|
||||
/* Called to update a row in the model */
|
||||
void gui_queue_model_update(struct queue *, unsigned int);
|
||||
/*
|
||||
* Called to update a row in the model
|
||||
* If track is NULL, then all rows will be updated
|
||||
*/
|
||||
void gui_model_update(struct playlist *, struct track *);
|
||||
|
||||
/* Called to change the queue represented by the model. */
|
||||
void gui_queue_model_set_queue(struct queue *);
|
||||
void gui_model_set_playlist(struct playlist *);
|
||||
|
||||
/* Called to get the queue currently attached to the model. */
|
||||
struct queue *gui_queue_model_get_queue(void);
|
||||
struct playlist *gui_model_get_playlist(void);
|
||||
|
||||
|
||||
/* Called to convert a GtkTreeIter into a struct track */
|
||||
static inline
|
||||
struct track *gui_queue_model_iter_get_track(GtkTreeIter *iter)
|
||||
static inline struct track *gui_model_iter_get_track(GtkTreeIter *iter)
|
||||
{
|
||||
g_return_val_if_fail(iter != NULL, NULL);
|
||||
g_return_val_if_fail(iter->user_data2 != NULL, NULL);
|
||||
return (struct track *)iter->user_data2;
|
||||
g_return_val_if_fail(iter->user_data != NULL, NULL);
|
||||
return playlist_iter_track(iter->user_data);
|
||||
}
|
||||
|
||||
/* Called to convert a GtkTreePath into a struct track */
|
||||
struct track *gui_queue_model_path_get_track(GtkTreePath *);
|
||||
struct track *gui_model_path_get_track(GtkTreePath *);
|
||||
|
||||
/* Called to get the runtime label. */
|
||||
static inline GtkLabel *gui_model_runtime()
|
||||
{
|
||||
return GTK_LABEL(gui_builder_widget("runtime"));
|
||||
}
|
||||
|
||||
#endif /* OCARINA_GUI_MODEL_H */
|
||||
|
|
|
@ -3,25 +3,29 @@
|
|||
*/
|
||||
#ifndef OCARINA_GUI_PLAYLIST_H
|
||||
#define OCARINA_GUI_PLAYLIST_H
|
||||
|
||||
#include <core/playlist.h>
|
||||
#include <gui/builder.h>
|
||||
#include <gui/playlists/artist.h>
|
||||
#include <gui/playlists/library.h>
|
||||
#include <gui/playlists/system.h>
|
||||
#include <gui/playlists/user.h>
|
||||
|
||||
/* Called to initialize the GUI playlist code. */
|
||||
void gui_playlist_init();
|
||||
|
||||
/* Called to get the currently selected playlist. */
|
||||
gchar *gui_playlist_cur();
|
||||
/* Called to access the right-click menu. */
|
||||
static inline GtkMenu *gui_rc_menu()
|
||||
{
|
||||
return GTK_MENU(gui_builder_widget("rc_menu"));
|
||||
}
|
||||
|
||||
/* Called to add a library playlist. */
|
||||
void gui_playlist_add_library(struct library *);
|
||||
/* Called to acces the "Add to Other Playlist" menu item. */
|
||||
static inline GtkMenuItem *gui_rc_add_to_other()
|
||||
{
|
||||
return GTK_MENU_ITEM(gui_builder_widget("rc_add_to_other"));
|
||||
}
|
||||
|
||||
/* Called to add an artist playlist. */
|
||||
void gui_playlist_add_artist(struct artist *);
|
||||
|
||||
/* Called to add a user playlist. */
|
||||
void gui_playlist_add_user(struct playlist *);
|
||||
|
||||
/* Playlist operations passed to core_init() */
|
||||
extern struct queue_ops playlist_ops;
|
||||
/* Playlist callbacks passed to core_init() */
|
||||
extern struct playlist_callbacks playlist_cb;
|
||||
|
||||
#endif /* OCARINA_GUI_PLAYLIST_H */
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_GUI_PLAYLISTS_ARTIST_H
|
||||
#define OCARINA_GUI_PLAYLISTS_ARTIST_H
|
||||
|
||||
/* Called to initialize GUI artist playlists. */
|
||||
void gui_pl_artist_init();
|
||||
|
||||
/* Called to add an artist playlist. */
|
||||
bool gui_pl_artist_add(struct playlist *);
|
||||
|
||||
/* Called to update an artist playlist. */
|
||||
void gui_pl_artist_update(struct playlist *);
|
||||
|
||||
/* Called to select an artist playlist. */
|
||||
void gui_pl_artist_select(struct playlist *);
|
||||
|
||||
#endif /* OCARINA_GUI_PLAYLISTS_ARTIST_H */
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2015 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_GUI_PLAYLISTS_LIBRARY_H
|
||||
#define OCARINA_GUI_PLAYLISTS_LIBRARY_H
|
||||
|
||||
/* Called to initialize GUI library playlists. */
|
||||
void gui_pl_library_init();
|
||||
|
||||
/* Called to add a library path. */
|
||||
struct playlist *gui_pl_library_add(const gchar *);
|
||||
|
||||
/* Called to update a library path. */
|
||||
void gui_pl_library_update(struct playlist *);
|
||||
|
||||
/* Called to select a library playlist. */
|
||||
void gui_pl_library_select(struct playlist *);
|
||||
|
||||
#endif /* OCARINA_GUI_PLAYLISTS_LIBRARY_H */
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_GUI_PLAYLISTS_SYSTEM_H
|
||||
#define OCARINA_GUI_PLAYLISTS_SYSTEM_H
|
||||
#include <gui/builder.h>
|
||||
|
||||
/* Called to initialize GUI system playlists. */
|
||||
void gui_pl_system_init();
|
||||
|
||||
/* Called to update a system playlist. */
|
||||
void gui_pl_system_update(struct playlist *);
|
||||
|
||||
/* Called to select a system playlist. */
|
||||
void gui_pl_system_select(struct playlist *);
|
||||
|
||||
/* Called to set favorites and hidden button states. */
|
||||
void gui_pl_system_track_loaded(struct track *);
|
||||
|
||||
/* Called to get the favorite toggle button. */
|
||||
static inline GtkToggleButton *gui_favorite_button()
|
||||
{
|
||||
return GTK_TOGGLE_BUTTON(gui_builder_widget("favorite_button"));
|
||||
}
|
||||
|
||||
/* Called to get the hidden toggle button. */
|
||||
static inline GtkToggleButton *gui_hide_button()
|
||||
{
|
||||
return GTK_TOGGLE_BUTTON(gui_builder_widget("hide_button"));
|
||||
}
|
||||
|
||||
#endif /* OCARINA_GUI_PLAYLISTS_SYSTEM_H */
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2015 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_GUI_PLAYLISTS_USER_H
|
||||
#define OCARINA_GUI_PLAYLISTS_USER_H
|
||||
|
||||
/* Called to initialize GUI user playlists. */
|
||||
void gui_pl_user_init();
|
||||
|
||||
/* Called to add a user playlist. */
|
||||
struct playlist *gui_pl_user_add(const gchar *);
|
||||
|
||||
/* Called to add a user playlist (with dialog prompt). */
|
||||
struct playlist *gui_pl_user_add_dialog(void);
|
||||
|
||||
/* Called to update a user playlist. */
|
||||
void gui_pl_user_update(struct playlist *);
|
||||
|
||||
/* Called to select a user playlist. */
|
||||
void gui_pl_user_select(struct playlist *);
|
||||
|
||||
/*
|
||||
* Called to get a (sorted) list of user playlists.
|
||||
* Note: The caller is responsible for freeing the list with g_list_free().
|
||||
*/
|
||||
GList *gui_pl_user_list(void);
|
||||
|
||||
#endif /* OCARINA_GUI_PLAYLISTS_USER_H */
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_GUI_QUEUE_H
|
||||
#define OCARINA_GUI_QUEUE_H
|
||||
|
||||
#include <core/queue.h>
|
||||
#include <gui/model.h>
|
||||
|
||||
enum gui_queue_flags {
|
||||
GQ_CAN_RANDOM = (1 << 0), /* Random button can be clicked. */
|
||||
};
|
||||
|
||||
struct gui_queue {
|
||||
unsigned int gq_flags;
|
||||
unsigned int gq_active;
|
||||
gchar *gq_text;
|
||||
gchar **gq_search;
|
||||
|
||||
struct playlist *gq_playlist;
|
||||
struct queue *gq_queue;
|
||||
};
|
||||
|
||||
|
||||
/* Called to initialize the gui_queue code. */
|
||||
void gui_queue_init(void);
|
||||
|
||||
/* Called to allocate a new struct gui_queue. */
|
||||
struct gui_queue *gui_queue_alloc(struct playlist *, struct queue *,
|
||||
const gchar *, unsigned int);
|
||||
|
||||
/* Called to free a struct gui_queue. */
|
||||
void gui_queue_free(struct queue *);
|
||||
|
||||
/* Called to access a the struct gui_queue attached to a queue. */
|
||||
static inline struct gui_queue *gui_queue(struct queue *queue)
|
||||
{
|
||||
return queue ? (struct gui_queue *)queue->q_private : NULL;
|
||||
}
|
||||
|
||||
/* Called to ask the struct gui_queue if it can change random flag. */
|
||||
static inline bool gui_queue_can_random(struct gui_queue *gq)
|
||||
{
|
||||
if (gq)
|
||||
return (gq->gq_flags & GQ_CAN_RANDOM) == GQ_CAN_RANDOM;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Called to set the correct state of the random and repeat buttons. */
|
||||
void gui_queue_show(struct gui_queue *);
|
||||
|
||||
#endif /* OCARINA_GUI_QUEUE_H */
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_GUI_QUEUE_LABEL_H
|
||||
#define OCARINA_GUI_QUEUE_LABEL_H
|
||||
|
||||
#include <core/queue.h>
|
||||
#include <gtkmm.h>
|
||||
|
||||
|
||||
class QueueLabel : public Gtk::HBox {
|
||||
protected:
|
||||
Glib::RefPtr<Gtk::Builder> _builder;
|
||||
queue *_queue;
|
||||
|
||||
public:
|
||||
QueueLabel(BaseObjectType *, const Glib::RefPtr<Gtk::Builder>);
|
||||
~QueueLabel();
|
||||
virtual void init(queue *);
|
||||
virtual void set_sensitive(bool) {};
|
||||
virtual void set_size() {};
|
||||
};
|
||||
|
||||
|
||||
|
||||
class TempLabel : public QueueLabel {
|
||||
public:
|
||||
Gtk::Label *temp_number;
|
||||
Gtk::Label *temp_size;
|
||||
Gtk::Button *temp_close;
|
||||
|
||||
TempLabel(BaseObjectType *, const Glib::RefPtr<Gtk::Builder>);
|
||||
void set_sensitive(bool);
|
||||
void set_size();
|
||||
};
|
||||
|
||||
#endif /* OCARINA_GUI_QUEUE_LABEL_H */
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_GUI_QUEUE_WINDOW_H
|
||||
#define OCARINA_GUI_QUEUE_WINDOW_H
|
||||
|
||||
extern "C" {
|
||||
#include <core/containers/set.h>
|
||||
#include <gui/queue.h>
|
||||
}
|
||||
#include <gtkmm.h>
|
||||
|
||||
class QueueWindow : public Gtk::ScrolledWindow {
|
||||
public:
|
||||
Glib::RefPtr<Gtk::Builder> _builder;
|
||||
|
||||
Gtk::TreeView *q_treeview;
|
||||
|
||||
QueueWindow(BaseObjectType *, const Glib::RefPtr<Gtk::Builder>);
|
||||
~QueueWindow();
|
||||
void init(queue *);
|
||||
|
||||
bool on_key_press(GdkEventKey *);
|
||||
};
|
||||
|
||||
#endif /* OCARINA_GUI_QUEUE_WINDOW_H */
|
|
@ -3,9 +3,122 @@
|
|||
*/
|
||||
#ifndef OCARINA_GUI_SIDEBAR_H
|
||||
#define OCARINA_GUI_SIDEBAR_H
|
||||
|
||||
#include <core/playlist.h>
|
||||
#include <gui/builder.h>
|
||||
|
||||
/* Called to initialize the sidebar. */
|
||||
void gui_sidebar_init();
|
||||
|
||||
/* Called to set an iterator to the currently displayed playlist. */
|
||||
gboolean gui_sidebar_iter_current(GtkTreeIter *);
|
||||
|
||||
/* Called to set an iterator to the first playlist. */
|
||||
gboolean gui_sidebar_iter_first(GtkTreeIter *);
|
||||
|
||||
/* Called to advance the iterator to the next playlist. */
|
||||
gboolean gui_sidebar_iter_next(GtkTreeIter *);
|
||||
|
||||
/* Called to set an iterator to the first child of the parent iterator. */
|
||||
gboolean gui_sidebar_iter_down(GtkTreeIter *, GtkTreeIter *);
|
||||
|
||||
/*
|
||||
* Called to find the name of the playlist at the given iterator.
|
||||
* NOTE: This function returns a new string that must be freed with g_free().
|
||||
*/
|
||||
gchar *gui_sidebar_iter_name(GtkTreeIter *);
|
||||
|
||||
/* Called to find the type of the playlist at the given iterator. */
|
||||
enum playlist_type_t gui_sidebar_iter_type(GtkTreeIter *);
|
||||
|
||||
/* Called to find the editable state of the playlist at the given iterator. */
|
||||
bool gui_sidebar_iter_editable(GtkTreeIter *);
|
||||
|
||||
/* Called to find the playlist at the given iterator. */
|
||||
struct playlist *gui_sidebar_iter_playlist(GtkTreeIter *);
|
||||
|
||||
/* Called to add a playlist at the current iterator. */
|
||||
void gui_sidebar_iter_add(GtkTreeIter *, struct playlist *, const gchar *);
|
||||
|
||||
/* Called to add a child playlist to the current iterator in sorted order. */
|
||||
void gui_sidebar_iter_sort_child(GtkTreeIter *, struct playlist *,
|
||||
const gchar *);
|
||||
|
||||
/* Called to append a child playlist to the current iterator. */
|
||||
void gui_sidebar_iter_append_child(GtkTreeIter *, struct playlist *,
|
||||
const gchar *);
|
||||
|
||||
/* Called to update the playlist at the current iterator. */
|
||||
void gui_sidebar_iter_update_playlist(GtkTreeIter *, struct playlist *);
|
||||
void gui_sidebar_iter_update(GtkTreeIter *);
|
||||
|
||||
/* Called to select the row at the current iterator. */
|
||||
void gui_sidebar_iter_select(GtkTreeIter *);
|
||||
|
||||
/* Called to set the current iterator as editable. */
|
||||
bool gui_sidebar_iter_set_editable(GtkTreeIter *, bool);
|
||||
|
||||
/* Called to set the playlist at the given iterator as the default. */
|
||||
void gui_sidebar_filter_path_select(GtkTreePath *);
|
||||
|
||||
/* Called to expand or collapse sidebar rows from the user's settings. */
|
||||
void gui_sidebar_filter_set_expand(GtkTreeIter *);
|
||||
|
||||
/* Called when a playlist treeview row is expanded or collapsed. */
|
||||
void gui_sidebar_filter_row_expanded(GtkTreeIter *, bool);
|
||||
|
||||
/*
|
||||
* Called to find a playlist with the given name and type,
|
||||
* starting from the current iterator position.
|
||||
*/
|
||||
gboolean gui_sidebar_iter_find(GtkTreeIter *, const gchar *,
|
||||
enum playlist_type_t);
|
||||
|
||||
/* Called to set the a GtkTreeIter to the row at path string */
|
||||
gboolean gui_sidebar_iter_from_string(const gchar *, GtkTreeIter *);
|
||||
|
||||
/* Called to set the GtkTreeIter to the row at (x, y) */
|
||||
gboolean gui_sidebar_iter_from_xy(gint, gint, GtkTreeIter *);
|
||||
|
||||
/* Called to get the sidebar widget. */
|
||||
static inline GtkPaned *gui_sidebar()
|
||||
{
|
||||
return GTK_PANED(gui_builder_widget("sidebar"));
|
||||
}
|
||||
|
||||
/* Called to get the sidebar treestore. */
|
||||
static inline GtkTreeStore *gui_sidebar_store()
|
||||
{
|
||||
return GTK_TREE_STORE(gui_builder_object("sidebar_store"));
|
||||
}
|
||||
|
||||
/* Called to get the sidebar treemodel. */
|
||||
static inline GtkTreeModel *gui_sidebar_model()
|
||||
{
|
||||
return GTK_TREE_MODEL(gui_builder_object("sidebar_store"));
|
||||
}
|
||||
|
||||
/* Called to get the sidebar filter model. */
|
||||
static inline GtkTreeModelFilter *gui_sidebar_filter()
|
||||
{
|
||||
return GTK_TREE_MODEL_FILTER(gui_builder_object("sidebar_filter"));
|
||||
}
|
||||
|
||||
/* Called to get the sidebar treeview. */
|
||||
static inline GtkTreeView *gui_sidebar_treeview()
|
||||
{
|
||||
return GTK_TREE_VIEW(gui_builder_widget("sidebar_treeview"));
|
||||
}
|
||||
|
||||
/* Called to get the sidebar right-click menu. */
|
||||
static inline GtkMenu *gui_sidebar_menu()
|
||||
{
|
||||
return GTK_MENU(gui_builder_widget("rc_sidebar"));
|
||||
}
|
||||
|
||||
/* Called to get the random button. */
|
||||
static inline GtkToggleButton *gui_random_button()
|
||||
{
|
||||
return GTK_TOGGLE_BUTTON(gui_builder_widget("random_button"));
|
||||
}
|
||||
|
||||
#endif /* OCARINA_GUI_SIDEBAR_H */
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_GUI_TREEVIEW_H
|
||||
#define OCARINA_GUI_TREEVIEW_H
|
||||
#include <gui/builder.h>
|
||||
|
||||
/* Called to initialize the treeview widget. */
|
||||
void gui_treeview_init();
|
||||
|
||||
/* Called to deinitialize the treeview layer. */
|
||||
void gui_treeview_deinit();
|
||||
|
||||
/* Called to set the current playlist. */
|
||||
void gui_treeview_set_playlist(struct playlist *);
|
||||
|
||||
/* Called to scroll the treeview to the current track. */
|
||||
void gui_treeview_scroll();
|
||||
|
||||
/* Called to select a path from (x, y) screen coodinates. */
|
||||
void gui_treeview_select_path_at_pos(unsigned int x, unsigned int y);
|
||||
|
||||
/*
|
||||
* Called to get a list of selected tracks.
|
||||
* NOTE: The caller is responsible for freeing the list with g_list_free().
|
||||
*/
|
||||
GList *gui_treeview_list_selected_tracks(void);
|
||||
|
||||
/* Called to access the treeview widget. */
|
||||
static inline GtkTreeView *gui_treeview()
|
||||
{
|
||||
return GTK_TREE_VIEW(gui_builder_widget("treeview"));
|
||||
}
|
||||
|
||||
/* Called to access the treview selection. */
|
||||
static inline GtkTreeSelection *gui_treeview_selection()
|
||||
{
|
||||
return gtk_tree_view_get_selection(gui_treeview());
|
||||
}
|
||||
|
||||
/* Called to access the sorting display widget. */
|
||||
static inline GtkLabel *gui_sorting()
|
||||
{
|
||||
return GTK_LABEL(gui_builder_widget("sorting"));
|
||||
}
|
||||
|
||||
#endif /* OCARINA_GUI_TREEVIEW_H */
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_GUI_VIEW_H
|
||||
#define OCARINA_GUI_VIEW_H
|
||||
|
||||
#include <core/queue.h>
|
||||
|
||||
/* Called to initialize structures needed by the treeview. */
|
||||
void gui_view_init();
|
||||
|
||||
/* Called to scroll the GUI treeview to the queue's current position. */
|
||||
void gui_view_scroll();
|
||||
|
||||
/* Called to get the GtkTreeModelFilter */
|
||||
GtkTreeModelFilter *gui_view_get_filter(void);
|
||||
|
||||
/* Called to set the currently displayed model. */
|
||||
void gui_view_set_queue(struct queue *);
|
||||
|
||||
#endif /* OCARINA_GUI_VIEW_H */
|
|
@ -3,6 +3,7 @@
|
|||
*/
|
||||
#ifndef OCARINA_GUI_WINDOW_H
|
||||
#define OCARINA_GUI_WINDOW_H
|
||||
#include <gui/builder.h>
|
||||
|
||||
/* Called to initialize the main window. */
|
||||
void gui_window_init(const gchar *);
|
||||
|
@ -10,4 +11,10 @@ void gui_window_init(const gchar *);
|
|||
/* Called to deinitialize the main window. */
|
||||
void gui_window_deinit();
|
||||
|
||||
/* Called to get a pointer to the main window. */
|
||||
static inline GtkWindow *gui_window()
|
||||
{
|
||||
return GTK_WINDOW(gui_builder_widget("window"));
|
||||
}
|
||||
|
||||
#endif /* OCARINA_GUI_WINDOW_H */
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_TESTS_LOOP_H
|
||||
#define OCARINA_TESTS_LOOP_H
|
||||
|
||||
static GMainLoop *__test_main_loop;
|
||||
|
||||
static int __test_loop_on_idle(gpointer data)
|
||||
{
|
||||
g_main_loop_quit(__test_main_loop);
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void test_loop_init()
|
||||
{
|
||||
__test_main_loop = g_main_loop_new(NULL, FALSE);
|
||||
g_idle_add(__test_loop_on_idle, NULL);
|
||||
}
|
||||
|
||||
static void test_loop_deinit()
|
||||
{
|
||||
g_main_loop_unref(__test_main_loop);
|
||||
}
|
||||
|
||||
static void test_main_loop()
|
||||
{
|
||||
g_main_loop_run(__test_main_loop);
|
||||
}
|
||||
#endif /* OCARINA_TESTS_LOOP_H */
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.20.0 -->
|
||||
<!-- Generated with glade 3.20.2 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
|
@ -30,9 +30,44 @@
|
|||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-new</property>
|
||||
</object>
|
||||
<object class="GtkMenu" id="o_menu">
|
||||
<object class="GtkImage" id="image24">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">audio-x-generic</property>
|
||||
</object>
|
||||
<object class="GtkMenu" id="rc_menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label" translatable="yes">Add to Queued Tracks</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">image24</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="__gui_playlist_add_queued" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label" translatable="yes">Add to Favorites</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">image20</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="__gui_playlist_add_favorites" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label" translatable="yes">Add to Hidden</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">image19</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="__gui_playlist_add_hidden" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label" translatable="yes">Add to New Playlist</property>
|
||||
|
@ -40,31 +75,11 @@
|
|||
<property name="can_focus">False</property>
|
||||
<property name="image">image23</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="__view_rc_add_new" swapped="no"/>
|
||||
<signal name="activate" handler="__gui_playlist_add_user" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="o_add_to_favorites">
|
||||
<property name="label" translatable="yes">Add to Favorites</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">image20</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="__view_rc_add_favorites" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="o_add_to_hidden">
|
||||
<property name="label" translatable="yes">Add to Hidden</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">image19</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="__view_rc_add_hidden" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="o_add_to_other">
|
||||
<object class="GtkImageMenuItem" id="rc_add_to_other">
|
||||
<property name="label" translatable="yes">Add to Other Playlist</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -73,12 +88,69 @@
|
|||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="image25">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">dialog-ok</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="image26">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-delete</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="image27">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-edit</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkMenu" id="rc_sidebar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label" translatable="yes">Select Playlist</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">image25</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="__gui_sidebar_select" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="rc_sidebar_rename">
|
||||
<property name="label" translatable="yes">Rename Playlist</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">image27</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="__gui_sidebar_rename" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem">
|
||||
<property name="label" translatable="yes">Delete Playlist</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">image26</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="__gui_sidebar_delete" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="image3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">folder-new</property>
|
||||
</object>
|
||||
<object class="GtkTreeStore" id="o_playlist_store">
|
||||
<object class="GtkAdjustment" id="seek">
|
||||
<property name="upper">100000000000</property>
|
||||
<property name="step_increment">1000000000</property>
|
||||
<property name="page_increment">10000000000</property>
|
||||
</object>
|
||||
<object class="GtkTreeStore" id="sidebar_store">
|
||||
<columns>
|
||||
<!-- column-name Image -->
|
||||
<column type="gchararray"/>
|
||||
|
@ -86,22 +158,20 @@
|
|||
<column type="gchararray"/>
|
||||
<!-- column-name Type -->
|
||||
<column type="guint"/>
|
||||
<!-- column-name Editable -->
|
||||
<column type="gboolean"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkTreeModelFilter" id="o_playlist_filter">
|
||||
<property name="child_model">o_playlist_store</property>
|
||||
<object class="GtkTreeModelFilter" id="sidebar_filter">
|
||||
<property name="child_model">sidebar_store</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="o_progress">
|
||||
<property name="upper">100000000000</property>
|
||||
<property name="step_increment">1000000000</property>
|
||||
<property name="page_increment">10000000000</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="o_window">
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="title" translatable="yes">Ocarina 6.1.3</property>
|
||||
<property name="default_width">1024</property>
|
||||
<property name="default_height">615</property>
|
||||
<property name="gravity">static</property>
|
||||
<signal name="configure-event" handler="__window_configure" swapped="no"/>
|
||||
<signal name="window-state-event" handler="__window_state" swapped="no"/>
|
||||
<child>
|
||||
|
@ -119,7 +189,7 @@
|
|||
<property name="valign">center</property>
|
||||
<property name="layout_style">start</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="o_prev">
|
||||
<object class="GtkButton" id="prev_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
|
@ -127,7 +197,7 @@
|
|||
<property name="tooltip_text" translatable="yes">Previous track</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="can-activate-accel" handler="__audio_can_accel" swapped="no"/>
|
||||
<signal name="can-activate-accel" handler="__gui_audio_can_accel" swapped="no"/>
|
||||
<signal name="clicked" handler="audio_prev" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image4">
|
||||
|
@ -142,8 +212,8 @@
|
|||
<property name="icon_name">media-skip-backward</property>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="n" signal="clicked" modifiers="GDK_SHIFT_MASK"/>
|
||||
<accelerator key="AudioPrev" signal="clicked"/>
|
||||
<accelerator key="n" signal="clicked" modifiers="GDK_SHIFT_MASK"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -153,7 +223,7 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="o_play">
|
||||
<object class="GtkButton" id="play_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
|
@ -161,7 +231,7 @@
|
|||
<property name="tooltip_text" translatable="yes">Start playback</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="can-activate-accel" handler="__audio_can_accel" swapped="no"/>
|
||||
<signal name="can-activate-accel" handler="__gui_audio_can_accel" swapped="no"/>
|
||||
<signal name="clicked" handler="audio_play" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image1">
|
||||
|
@ -177,8 +247,8 @@
|
|||
<property name="icon_size">5</property>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="space" signal="clicked"/>
|
||||
<accelerator key="AudioPlay" signal="clicked"/>
|
||||
<accelerator key="space" signal="clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -188,15 +258,15 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="o_pause">
|
||||
<object class="GtkButton" id="pause_button">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Pause playback</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="can-activate-accel" handler="__audio_can_accel" swapped="no"/>
|
||||
<signal name="clicked" handler="audio_pause" swapped="no"/>
|
||||
<signal name="can-activate-accel" handler="__gui_audio_can_accel" swapped="no"/>
|
||||
<signal name="clicked" handler="__gui_audio_pause" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image2">
|
||||
<property name="visible">True</property>
|
||||
|
@ -211,8 +281,8 @@
|
|||
<property name="icon_size">5</property>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="space" signal="clicked"/>
|
||||
<accelerator key="AudioPlay" signal="clicked"/>
|
||||
<accelerator key="space" signal="clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -222,7 +292,7 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="o_next">
|
||||
<object class="GtkButton" id="next_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
|
@ -230,7 +300,7 @@
|
|||
<property name="tooltip_text" translatable="yes">Next track</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="can-activate-accel" handler="__audio_can_accel" swapped="no"/>
|
||||
<signal name="can-activate-accel" handler="__gui_audio_can_accel" swapped="no"/>
|
||||
<signal name="clicked" handler="audio_next" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image5">
|
||||
|
@ -245,8 +315,8 @@
|
|||
<property name="icon_name">media-skip-forward</property>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="n" signal="clicked"/>
|
||||
<accelerator key="AudioNext" signal="clicked"/>
|
||||
<accelerator key="n" signal="clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -268,11 +338,12 @@
|
|||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="relief">none</property>
|
||||
<signal name="clicked" handler="__artwork_select_cover" swapped="no"/>
|
||||
<signal name="clicked" handler="__gui_artwork_select_cover" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="o_cover">
|
||||
<object class="GtkImage" id="artwork">
|
||||
<property name="height_request">100</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">image-missing</property>
|
||||
<property name="icon_size">6</property>
|
||||
|
@ -286,7 +357,7 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="o_tags">
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
|
@ -305,7 +376,7 @@
|
|||
<property name="column_spacing">5</property>
|
||||
<property name="row_homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="o_title">
|
||||
<object class="GtkLabel" id="title_tag">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
|
@ -320,7 +391,7 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="o_artist">
|
||||
<object class="GtkLabel" id="artist_tag">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
|
@ -334,7 +405,7 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="o_album">
|
||||
<object class="GtkLabel" id="album_tag">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
|
@ -389,14 +460,14 @@
|
|||
<property name="valign">end</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="o_random">
|
||||
<object class="GtkToggleButton" id="random_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Randomly pick tracks</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="toggled" handler="__queue_random" swapped="no"/>
|
||||
<signal name="toggled" handler="__gui_sidebar_random_toggled" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image9">
|
||||
<property name="visible">True</property>
|
||||
|
@ -415,7 +486,7 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVolumeButton" id="o_volume">
|
||||
<object class="GtkVolumeButton" id="volume_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
|
@ -430,7 +501,7 @@ audio-volume-high
|
|||
audio-volume-low
|
||||
audio-volume-medium</property>
|
||||
<property name="use_symbolic">False</property>
|
||||
<signal name="value-changed" handler="__audio_volume_changed" swapped="no"/>
|
||||
<signal name="value-changed" handler="__gui_audio_volume_changed" swapped="no"/>
|
||||
<child internal-child="plus_button">
|
||||
<object class="GtkButton">
|
||||
<property name="can_focus">True</property>
|
||||
|
@ -461,7 +532,6 @@ audio-volume-medium</property>
|
|||
<packing>
|
||||
<property name="left_attach">3</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -471,7 +541,7 @@ audio-volume-medium</property>
|
|||
<property name="hexpand">True</property>
|
||||
<property name="spacing">3</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="o_position">
|
||||
<object class="GtkLabel" id="position">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="has_tooltip">True</property>
|
||||
|
@ -487,16 +557,16 @@ audio-volume-medium</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScale" id="o_seek">
|
||||
<object class="GtkScale">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="adjustment">o_progress</property>
|
||||
<property name="adjustment">seek</property>
|
||||
<property name="round_digits">0</property>
|
||||
<property name="draw_value">False</property>
|
||||
<property name="value_pos">left</property>
|
||||
<signal name="change-value" handler="__audio_seek" swapped="no"/>
|
||||
<signal name="change-value" handler="__gui_audio_seek" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
|
@ -505,7 +575,7 @@ audio-volume-medium</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="o_duration">
|
||||
<object class="GtkLabel" id="duration">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
|
@ -527,11 +597,80 @@ audio-volume-medium</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">Pause after</property>
|
||||
<property name="valign">center</property>
|
||||
<child>
|
||||
<object class="GtkEntry" id="pause_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="text" translatable="yes">Paused</property>
|
||||
<signal name="activate" handler="__gui_audio_pause_change_text" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="pause_down">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="can-activate-accel" handler="__gui_audio_can_accel" swapped="no"/>
|
||||
<signal name="clicked" handler="__gui_audio_pause_dec" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">list-remove-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="minus" signal="clicked"/>
|
||||
<accelerator key="KP_Subtract" signal="clicked"/>
|
||||
<style>
|
||||
<class name="down"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="pause_up">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="can-activate-accel" handler="__gui_audio_can_accel" swapped="no"/>
|
||||
<signal name="clicked" handler="__gui_audio_pause_inc" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">list-add-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="plus" signal="clicked"/>
|
||||
<accelerator key="KP_Add" signal="clicked"/>
|
||||
<style>
|
||||
<class name="up"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<style>
|
||||
<class name="linked"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">3</property>
|
||||
|
@ -540,132 +679,14 @@ audio-volume-medium</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="o_pause_after">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="active">0</property>
|
||||
<items>
|
||||
<item translatable="yes">(disabled)</item>
|
||||
<item translatable="yes">this track</item>
|
||||
<item translatable="yes">next track</item>
|
||||
<item translatable="yes">2 tracks</item>
|
||||
<item translatable="yes">3 tracks</item>
|
||||
<item translatable="yes">4 tracks</item>
|
||||
<item translatable="yes">5 tracks</item>
|
||||
<item translatable="yes">6 tracks</item>
|
||||
<item translatable="yes">7 tracks</item>
|
||||
<item translatable="yes">8 tracks</item>
|
||||
<item translatable="yes">9 tracks</item>
|
||||
<item translatable="yes">10 tracks</item>
|
||||
<item translatable="yes">11 tracks</item>
|
||||
<item translatable="yes">12 tracks</item>
|
||||
<item translatable="yes">13 tracks</item>
|
||||
<item translatable="yes">14 tracks</item>
|
||||
<item translatable="yes">15 tracks</item>
|
||||
<item translatable="yes">16 tracks</item>
|
||||
<item translatable="yes">17 tracks</item>
|
||||
<item translatable="yes">18 tracks</item>
|
||||
<item translatable="yes">19 tracks</item>
|
||||
<item translatable="yes">20 tracks</item>
|
||||
<item translatable="yes">21 tracks</item>
|
||||
<item translatable="yes">22 tracks</item>
|
||||
<item translatable="yes">23 tracks</item>
|
||||
<item translatable="yes">24 tracks</item>
|
||||
<item translatable="yes">25 tracks</item>
|
||||
<item translatable="yes">26 tracks</item>
|
||||
<item translatable="yes">27 tracks</item>
|
||||
<item translatable="yes">28 tracks</item>
|
||||
<item translatable="yes">29 tracks</item>
|
||||
<item translatable="yes">30 tracks</item>
|
||||
<item translatable="yes">31 tracks</item>
|
||||
<item translatable="yes">32 tracks</item>
|
||||
<item translatable="yes">33 tracks</item>
|
||||
<item translatable="yes">34 tracks</item>
|
||||
<item translatable="yes">35 tracks</item>
|
||||
<item translatable="yes">36 tracks</item>
|
||||
<item translatable="yes">37 tracks</item>
|
||||
<item translatable="yes">38 tracks</item>
|
||||
<item translatable="yes">39 tracks</item>
|
||||
<item translatable="yes">40 tracks</item>
|
||||
<item translatable="yes">41 tracks</item>
|
||||
<item translatable="yes">42 tracks</item>
|
||||
<item translatable="yes">43 tracks</item>
|
||||
<item translatable="yes">44 tracks</item>
|
||||
<item translatable="yes">45 tracks</item>
|
||||
<item translatable="yes">46 tracks</item>
|
||||
<item translatable="yes">47 tracks</item>
|
||||
<item translatable="yes">48 tracks</item>
|
||||
<item translatable="yes">49 tracks</item>
|
||||
<item translatable="yes">50 tracks</item>
|
||||
<item translatable="yes">51 tracks</item>
|
||||
<item translatable="yes">52 tracks</item>
|
||||
<item translatable="yes">53 tracks</item>
|
||||
<item translatable="yes">54 tracks</item>
|
||||
<item translatable="yes">55 tracks</item>
|
||||
<item translatable="yes">56 tracks</item>
|
||||
<item translatable="yes">57 tracks</item>
|
||||
<item translatable="yes">58 tracks</item>
|
||||
<item translatable="yes">59 tracks</item>
|
||||
<item translatable="yes">60 tracks</item>
|
||||
<item translatable="yes">61 tracks</item>
|
||||
<item translatable="yes">62 tracks</item>
|
||||
<item translatable="yes">63 tracks</item>
|
||||
<item translatable="yes">64 tracks</item>
|
||||
<item translatable="yes">65 tracks</item>
|
||||
<item translatable="yes">66 tracks</item>
|
||||
<item translatable="yes">67 tracks</item>
|
||||
<item translatable="yes">68 tracks</item>
|
||||
<item translatable="yes">69 tracks</item>
|
||||
<item translatable="yes">70 tracks</item>
|
||||
<item translatable="yes">71 tracks</item>
|
||||
<item translatable="yes">72 tracks</item>
|
||||
<item translatable="yes">73 tracks</item>
|
||||
<item translatable="yes">74 tracks</item>
|
||||
<item translatable="yes">75 tracks</item>
|
||||
<item translatable="yes">76 tracks</item>
|
||||
<item translatable="yes">77 tracks</item>
|
||||
<item translatable="yes">78 tracks</item>
|
||||
<item translatable="yes">79 tracks</item>
|
||||
<item translatable="yes">80 tracks</item>
|
||||
<item translatable="yes">81 tracks</item>
|
||||
<item translatable="yes">82 tracks</item>
|
||||
<item translatable="yes">83 tracks</item>
|
||||
<item translatable="yes">84 tracks</item>
|
||||
<item translatable="yes">85 tracks</item>
|
||||
<item translatable="yes">86 tracks</item>
|
||||
<item translatable="yes">87 tracks</item>
|
||||
<item translatable="yes">88 tracks</item>
|
||||
<item translatable="yes">89 tracks</item>
|
||||
<item translatable="yes">90 tracks</item>
|
||||
<item translatable="yes">91 tracks</item>
|
||||
<item translatable="yes">92 tracks</item>
|
||||
<item translatable="yes">93 tracks</item>
|
||||
<item translatable="yes">94 tracks</item>
|
||||
<item translatable="yes">95 tracks</item>
|
||||
<item translatable="yes">96 tracks</item>
|
||||
<item translatable="yes">97 tracks</item>
|
||||
<item translatable="yes">98 tracks</item>
|
||||
<item translatable="yes">99 tracks</item>
|
||||
</items>
|
||||
<signal name="changed" handler="__audio_pause_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">4</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="height">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned" id="o_sidebar">
|
||||
<object class="GtkPaned" id="sidebar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="position">140</property>
|
||||
<property name="position_set">True</property>
|
||||
<property name="wide_handle">True</property>
|
||||
<signal name="notify::position" handler="__sidebar_resize" swapped="no"/>
|
||||
<signal name="notify::position" handler="__gui_sidebar_resized" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkBox" id="box5">
|
||||
<property name="visible">True</property>
|
||||
|
@ -681,14 +702,14 @@ audio-volume-medium</property>
|
|||
<property name="spacing">5</property>
|
||||
<property name="layout_style">expand</property>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="o_favorite">
|
||||
<object class="GtkToggleButton" id="favorite_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Add this track to Favorites playlist</property>
|
||||
<property name="inconsistent">True</property>
|
||||
<signal name="toggled" handler="__audio_favorite" swapped="no"/>
|
||||
<signal name="toggled" handler="__gui_pl_system_favorite_toggled" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image8">
|
||||
<property name="visible">True</property>
|
||||
|
@ -704,13 +725,13 @@ audio-volume-medium</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="o_hide">
|
||||
<object class="GtkToggleButton" id="hide_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Hide this track from the Collection</property>
|
||||
<signal name="toggled" handler="__audio_hide" swapped="no"/>
|
||||
<signal name="toggled" handler="__gui_pl_system_hide_toggled" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image14">
|
||||
<property name="visible">True</property>
|
||||
|
@ -739,18 +760,23 @@ audio-volume-medium</property>
|
|||
<property name="vexpand">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="o_playlist_view">
|
||||
<object class="GtkTreeView" id="sidebar_treeview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">o_playlist_filter</property>
|
||||
<property name="model">sidebar_filter</property>
|
||||
<property name="headers_visible">False</property>
|
||||
<property name="search_column">1</property>
|
||||
<property name="enable_tree_lines">True</property>
|
||||
<signal name="key-press-event" handler="__playlist_keypress" swapped="no"/>
|
||||
<signal name="row-activated" handler="__playlist_row_activated" swapped="no"/>
|
||||
<signal name="button-press-event" handler="__gui_sidebar_button_press" swapped="no"/>
|
||||
<signal name="drag-data-received" handler="__gui_playlist_drag_data_received" swapped="no"/>
|
||||
<signal name="drag-drop" handler="__gui_treeview_drag_drop" swapped="no"/>
|
||||
<signal name="key-press-event" handler="__gui_sidebar_keypress" swapped="no"/>
|
||||
<signal name="row-activated" handler="__gui_playlist_row_activated" swapped="no"/>
|
||||
<signal name="row-collapsed" handler="__gui_playlist_row_collapsed" swapped="no"/>
|
||||
<signal name="row-expanded" handler="__gui_playlist_row_expanded" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection3">
|
||||
<signal name="changed" handler="__playlist_selection_changed" swapped="no"/>
|
||||
<signal name="changed" handler="__gui_sidebar_selection_changed" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -770,8 +796,12 @@ audio-volume-medium</property>
|
|||
<object class="GtkTreeViewColumn" id="treeviewcolumn3">
|
||||
<property name="title" translatable="yes">column</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext1"/>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext1">
|
||||
<signal name="edited" handler="__gui_pl_user_edited" swapped="no"/>
|
||||
<signal name="editing-started" handler="__gui_pl_user_editing_started" swapped="no"/>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="editable">3</attribute>
|
||||
<attribute name="markup">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
|
@ -793,7 +823,7 @@ audio-volume-medium</property>
|
|||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="image">image3</property>
|
||||
<signal name="clicked" handler="__collection_choose" swapped="no"/>
|
||||
<signal name="clicked" handler="__gui_pl_library_choose" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -804,7 +834,7 @@ audio-volume-medium</property>
|
|||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">False</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -834,7 +864,7 @@ audio-volume-medium</property>
|
|||
<item translatable="yes">Genre</item>
|
||||
<item translatable="yes">Title</item>
|
||||
</items>
|
||||
<signal name="changed" handler="__queue_filter_how_changed" swapped="no"/>
|
||||
<signal name="changed" handler="__gui_filter_how_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -843,7 +873,7 @@ audio-volume-medium</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSearchEntry" id="o_search">
|
||||
<object class="GtkSearchEntry" id="filter_search">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="valign">center</property>
|
||||
|
@ -851,7 +881,8 @@ audio-volume-medium</property>
|
|||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">False</property>
|
||||
<property name="placeholder_text" translatable="yes">Type here to filter</property>
|
||||
<signal name="search-changed" handler="__queue_filter" swapped="no"/>
|
||||
<signal name="search-changed" handler="__gui_filter_search_changed" swapped="no"/>
|
||||
<signal name="search-changed" handler="gui_treeview_scroll" after="yes" swapped="no"/>
|
||||
<accelerator key="slash" signal="grab-focus"/>
|
||||
</object>
|
||||
<packing>
|
||||
|
@ -875,16 +906,18 @@ audio-volume-medium</property>
|
|||
<property name="vexpand">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="o_treeview">
|
||||
<object class="GtkTreeView" id="treeview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="enable_search">False</property>
|
||||
<property name="fixed_height_mode">True</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<property name="tooltip_column">9</property>
|
||||
<signal name="button-press-event" handler="__view_button_press" swapped="no"/>
|
||||
<signal name="key-press-event" handler="__view_keypress" swapped="no"/>
|
||||
<signal name="row-activated" handler="__view_row_activated" swapped="no"/>
|
||||
<signal name="button-press-event" handler="__gui_playlist_button_press" swapped="no"/>
|
||||
<signal name="drag-data-received" handler="__gui_treeview_drag_data_received" swapped="no"/>
|
||||
<signal name="drag-drop" handler="__gui_treeview_drag_drop" swapped="no"/>
|
||||
<signal name="key-press-event" handler="__gui_playlist_keypress" swapped="no"/>
|
||||
<signal name="row-activated" handler="__gui_treeview_row_activated" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection5">
|
||||
<property name="mode">multiple</property>
|
||||
|
@ -897,8 +930,6 @@ audio-volume-medium</property>
|
|||
<property name="fixed_width">20</property>
|
||||
<property name="title" translatable="yes">#</property>
|
||||
<property name="clickable">True</property>
|
||||
<signal name="clicked" handler="__view_column_clicked" swapped="no"/>
|
||||
<signal name="notify::width" handler="__view_column_resized" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext4"/>
|
||||
<attributes>
|
||||
|
@ -915,8 +946,6 @@ audio-volume-medium</property>
|
|||
<property name="fixed_width">300</property>
|
||||
<property name="title" translatable="yes">Title</property>
|
||||
<property name="clickable">True</property>
|
||||
<signal name="clicked" handler="__view_column_clicked" swapped="no"/>
|
||||
<signal name="notify::width" handler="__view_column_resized" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext5"/>
|
||||
<attributes>
|
||||
|
@ -933,8 +962,6 @@ audio-volume-medium</property>
|
|||
<property name="fixed_width">60</property>
|
||||
<property name="title" translatable="yes">Length</property>
|
||||
<property name="clickable">True</property>
|
||||
<signal name="clicked" handler="__view_column_clicked" swapped="no"/>
|
||||
<signal name="notify::width" handler="__view_column_resized" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext6"/>
|
||||
<attributes>
|
||||
|
@ -951,8 +978,6 @@ audio-volume-medium</property>
|
|||
<property name="fixed_width">100</property>
|
||||
<property name="title" translatable="yes">Artist</property>
|
||||
<property name="clickable">True</property>
|
||||
<signal name="clicked" handler="__view_column_clicked" swapped="no"/>
|
||||
<signal name="notify::width" handler="__view_column_resized" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext7"/>
|
||||
<attributes>
|
||||
|
@ -969,8 +994,6 @@ audio-volume-medium</property>
|
|||
<property name="fixed_width">100</property>
|
||||
<property name="title" translatable="yes">Album</property>
|
||||
<property name="clickable">True</property>
|
||||
<signal name="clicked" handler="__view_column_clicked" swapped="no"/>
|
||||
<signal name="notify::width" handler="__view_column_resized" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext8"/>
|
||||
<attributes>
|
||||
|
@ -987,8 +1010,6 @@ audio-volume-medium</property>
|
|||
<property name="fixed_width">45</property>
|
||||
<property name="title" translatable="yes">Year</property>
|
||||
<property name="clickable">True</property>
|
||||
<signal name="clicked" handler="__view_column_clicked" swapped="no"/>
|
||||
<signal name="notify::width" handler="__view_column_resized" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext9"/>
|
||||
<attributes>
|
||||
|
@ -1005,8 +1026,6 @@ audio-volume-medium</property>
|
|||
<property name="fixed_width">100</property>
|
||||
<property name="title" translatable="yes">Genre</property>
|
||||
<property name="clickable">True</property>
|
||||
<signal name="clicked" handler="__view_column_clicked" swapped="no"/>
|
||||
<signal name="notify::width" handler="__view_column_resized" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext10"/>
|
||||
<attributes>
|
||||
|
@ -1023,8 +1042,6 @@ audio-volume-medium</property>
|
|||
<property name="fixed_width">60</property>
|
||||
<property name="title" translatable="yes">Count</property>
|
||||
<property name="clickable">True</property>
|
||||
<signal name="clicked" handler="__view_column_clicked" swapped="no"/>
|
||||
<signal name="notify::width" handler="__view_column_resized" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext11"/>
|
||||
<attributes>
|
||||
|
@ -1041,8 +1058,6 @@ audio-volume-medium</property>
|
|||
<property name="fixed_width">1</property>
|
||||
<property name="title" translatable="yes">Played</property>
|
||||
<property name="clickable">True</property>
|
||||
<signal name="clicked" handler="__view_column_clicked" swapped="no"/>
|
||||
<signal name="notify::width" handler="__view_column_resized" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext12"/>
|
||||
<attributes>
|
||||
|
@ -1062,7 +1077,7 @@ audio-volume-medium</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkProgressBar" id="o_idle_progress">
|
||||
<object class="GtkProgressBar" id="progress_bar">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">0%</property>
|
||||
<property name="valign">center</property>
|
||||
|
@ -1075,7 +1090,7 @@ audio-volume-medium</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="o_sorting">
|
||||
<object class="GtkLabel" id="sorting">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
|
@ -1086,7 +1101,7 @@ audio-volume-medium</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="o_runtime">
|
||||
<object class="GtkLabel" id="runtime">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
|
@ -1106,7 +1121,7 @@ audio-volume-medium</property>
|
|||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="width">5</property>
|
||||
<property name="width">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -1121,8 +1136,8 @@ audio-volume-medium</property>
|
|||
</object>
|
||||
<object class="GtkSizeGroup">
|
||||
<widgets>
|
||||
<widget name="o_random"/>
|
||||
<widget name="o_volume"/>
|
||||
<widget name="random_button"/>
|
||||
<widget name="volume_button"/>
|
||||
</widgets>
|
||||
</object>
|
||||
<object class="GtkSizeGroup">
|
||||
|
@ -1132,4 +1147,55 @@ audio-volume-medium</property>
|
|||
<widget name="filter_how"/>
|
||||
</widgets>
|
||||
</object>
|
||||
<object class="GtkPopover" id="pause_popover">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="relative_to">play_button</property>
|
||||
<property name="position">bottom</property>
|
||||
<child>
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_homogeneous">True</property>
|
||||
<property name="column_homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Cancel "pause after" configuration?</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="pause_popover_no">
|
||||
<property name="label" translatable="yes">No</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="__gui_audio_pause_popover_popdown" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="pause_popover_yes">
|
||||
<property name="label" translatable="yes">Yes</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="__gui_audio_pause_popover_clear" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
|
|
|
@ -5,5 +5,5 @@ date
|
|||
idle
|
||||
settings
|
||||
database
|
||||
queue
|
||||
playlist
|
||||
audio
|
||||
|
|
|
@ -13,7 +13,6 @@ core_unit_test(Settings)
|
|||
core_unit_test(Database)
|
||||
|
||||
add_subdirectory(tags/)
|
||||
core_unit_test(Queue)
|
||||
|
||||
core_unit_test(Playlist)
|
||||
add_subdirectory(playlists/)
|
||||
core_unit_test(Audio)
|
||||
|
|
|
@ -1,65 +1,54 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/audio.h>
|
||||
#include <core/idle.h>
|
||||
#include <core/playlist.h>
|
||||
#include <core/core.h>
|
||||
#include <tests/test.h>
|
||||
#include <tests/loop.h>
|
||||
|
||||
static unsigned int load_count = 0;
|
||||
static unsigned int state_count = 0;
|
||||
static int pause_count = 0;
|
||||
|
||||
|
||||
static bool test_audio_seek(gint64 pos)
|
||||
static unsigned int test_wait_state(void)
|
||||
{
|
||||
bool ret = audio_seek(pos);
|
||||
GstState state = audio_cur_state();
|
||||
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(test_audio_pipeline()));
|
||||
|
||||
while (state != GST_STATE_PAUSED && state != GST_STATE_PLAYING)
|
||||
state = audio_cur_state();
|
||||
return ret;
|
||||
g_usleep(G_USEC_PER_SEC / 15);
|
||||
while (gst_bus_have_pending(bus))
|
||||
test_main_loop();
|
||||
|
||||
gst_object_unref(bus);
|
||||
return state_count;
|
||||
}
|
||||
|
||||
static void test_send_error()
|
||||
{
|
||||
GstMessage *message;
|
||||
GError *error;
|
||||
|
||||
error = g_error_new(1, G_FILE_ERROR_BADF, "Simulated Error");
|
||||
message = gst_message_new_error(GST_OBJECT(test_audio_player()),
|
||||
error, "Fake error for testing");
|
||||
audio_error(message);
|
||||
|
||||
gst_message_unref(message);
|
||||
GError *error = g_error_new(1, G_FILE_ERROR_BADF, "Simulated Error");
|
||||
test_audio_error(error, "Fake error for testing");
|
||||
g_error_free(error);
|
||||
|
||||
}
|
||||
|
||||
static void test_audio_load(struct track *track) { load_count++; }
|
||||
static void test_change_state(GstState state) { state_count++; }
|
||||
static void test_config_pause(int n) { pause_count = n; }
|
||||
|
||||
static struct audio_ops test_audio_ops = {
|
||||
test_audio_load,
|
||||
test_change_state,
|
||||
test_config_pause,
|
||||
};
|
||||
|
||||
static struct core_init_data test_init_data = {
|
||||
.audio_ops = &test_audio_ops,
|
||||
static struct audio_callbacks test_audio_cb = {
|
||||
.audio_cb_load = test_audio_load,
|
||||
.audio_cb_state_change = test_change_state,
|
||||
.audio_cb_config_pause = test_config_pause,
|
||||
};
|
||||
|
||||
|
||||
static void test_init()
|
||||
{
|
||||
g_assert_null(test_audio_player());
|
||||
g_assert_null(test_audio_pipeline());
|
||||
g_assert_null(audio_cur_track());
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_NULL);
|
||||
g_assert_null(audio_next());
|
||||
|
||||
core_init(NULL, NULL, &test_init_data);
|
||||
core_init(NULL, NULL, NULL, &test_audio_cb, IDLE_SYNC);
|
||||
test_loop_init();
|
||||
|
||||
g_assert_false(audio_load(NULL));
|
||||
g_assert_null(audio_next());
|
||||
|
@ -73,6 +62,7 @@ static void test_init()
|
|||
g_assert_cmpuint(audio_get_volume(), ==, 100);
|
||||
g_assert_null(audio_cur_track());
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_NULL);
|
||||
g_assert_cmpint(audio_get_pause_count(), ==, -1);
|
||||
g_assert_cmpuint(load_count, ==, 0);
|
||||
g_assert_cmpuint(state_count, ==, 0);
|
||||
|
||||
|
@ -80,7 +70,7 @@ static void test_init()
|
|||
while (idle_run_task()) {};
|
||||
|
||||
g_assert_null(audio_cur_track());
|
||||
g_assert_nonnull(test_audio_player());
|
||||
g_assert_nonnull(test_audio_pipeline());
|
||||
}
|
||||
|
||||
static void test_playback()
|
||||
|
@ -93,9 +83,9 @@ static void test_playback()
|
|||
g_assert_true(audio_load(tracks[i]));
|
||||
else
|
||||
g_assert_false(audio_load(tracks[i]));
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, "History"), ==, 1);
|
||||
g_assert_cmpuint(load_count, ==, 1);
|
||||
g_assert_cmpuint(state_count, ==, 1);
|
||||
g_assert_cmpuint(playlist_size(playlist_lookup(PL_SYSTEM, "History")), ==, 1);
|
||||
g_assert_cmpuint(load_count, ==, 1);
|
||||
g_assert_cmpuint(test_wait_state(), ==, 1);
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_PLAYING);
|
||||
g_assert(audio_cur_track() == tracks[0]);
|
||||
g_assert_cmpuint(audio_duration(), ==, tracks[0]->tr_length * GST_SECOND);
|
||||
|
@ -110,19 +100,21 @@ static void test_playback()
|
|||
|
||||
g_assert_true(audio_pause());
|
||||
g_assert_false(audio_pause());
|
||||
g_assert_cmpuint(state_count, ==, 2);
|
||||
g_assert_cmpuint(test_wait_state(), ==, 2);
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_PAUSED);
|
||||
|
||||
g_assert_true(test_audio_seek(5 * GST_SECOND));
|
||||
g_assert_cmpuint(audio_position(), ==, 5 * GST_SECOND);
|
||||
g_assert_true(test_audio_seek(42 * GST_SECOND));
|
||||
g_assert_cmpuint(audio_position(), ==, 42 * GST_SECOND);
|
||||
g_assert_true(audio_seek(5 * GST_SECOND));
|
||||
g_assert_cmpuint(test_wait_state(), ==, 3);
|
||||
g_assert_cmpuint(audio_position(), ==, 5 * GST_SECOND);
|
||||
g_assert_true(audio_seek(42 * GST_SECOND));
|
||||
g_assert_cmpuint(test_wait_state(), ==, 4);
|
||||
g_assert_cmpuint(audio_position(), ==, 42 * GST_SECOND);
|
||||
|
||||
g_assert_true(audio_play());
|
||||
g_assert_false(audio_play());
|
||||
g_assert_cmpuint(state_count, ==, 3);
|
||||
g_assert_cmpuint(test_wait_state(), ==, 5);
|
||||
g_assert_true(audio_pause());
|
||||
g_assert_cmpuint(state_count, ==, 4);
|
||||
g_assert_cmpuint(test_wait_state(), ==, 6);
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_PAUSED);
|
||||
|
||||
/* Check duration again now that track is fully loaded. */
|
||||
|
@ -131,119 +123,168 @@ static void test_playback()
|
|||
|
||||
static void test_next()
|
||||
{
|
||||
struct queue *history_q = playlist_get_queue(PL_SYSTEM, "History");
|
||||
struct playlist *history = playlist_lookup(PL_SYSTEM, "History");
|
||||
int i;
|
||||
|
||||
state_count = 0;
|
||||
/* First, let's test getting tracks from a temporary queue. */
|
||||
playlist_add(PL_SYSTEM, "Queued Tracks", track_get(2));
|
||||
playlist_add(PL_SYSTEM, "Queued Tracks", track_get(1));
|
||||
playlist_add(PL_SYSTEM, "Queued Tracks", track_get(0));
|
||||
playlist_add(playlist_lookup(PL_SYSTEM, "Queued Tracks"), track_get(2));
|
||||
playlist_add(playlist_lookup(PL_SYSTEM, "Queued Tracks"), track_get(1));
|
||||
playlist_add(playlist_lookup(PL_SYSTEM, "Queued Tracks"), track_get(0));
|
||||
|
||||
for (i = 2; i >= 0; i--) {
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, "Queued Tracks"), ==, i + 1);
|
||||
g_assert_cmpuint(playlist_size(playlist_lookup(PL_SYSTEM, "Queued Tracks")), ==, i + 1);
|
||||
if (i > 0)
|
||||
g_assert_cmpuint(audio_next()->tr_track, ==, track_get(i)->tr_track);
|
||||
else /* Simulate an error. */
|
||||
test_send_error();
|
||||
g_assert(queue_at(history_q, 0) == track_get(i));
|
||||
g_assert(playlist_at(history, 0) == track_get(i));
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_PLAYING);
|
||||
g_assert(audio_cur_track() == track_get(i));
|
||||
}
|
||||
g_assert_cmpuint(state_count, ==, 3);
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, "Queued Tracks"), ==, 0);
|
||||
g_assert_cmpuint(test_wait_state(), ==, 3);
|
||||
g_assert_cmpuint(playlist_size(playlist_lookup(PL_SYSTEM, "Queued Tracks")), ==, 0);
|
||||
|
||||
/* Tracks should now be picked from the collection. */
|
||||
playlist_select(PL_SYSTEM, "Collection");
|
||||
playlist_select(playlist_lookup(PL_SYSTEM, "Collection"));
|
||||
for (i = 1; i <= 3; i++) {
|
||||
if (i < 3)
|
||||
g_assert_cmpuint(audio_next()->tr_track, ==, i);
|
||||
else /* Simulate an error. */
|
||||
test_send_error();
|
||||
g_assert_cmpuint(queue_at(history_q, 0)->tr_track, ==, i);
|
||||
g_assert_cmpuint(playlist_at(history, 0)->tr_track, ==, i);
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_PLAYING);
|
||||
g_assert_cmpuint(audio_cur_track()->tr_track, ==, i);
|
||||
}
|
||||
g_assert_cmpuint(state_count, ==, 6);
|
||||
g_assert_cmpuint(test_wait_state(), ==, 6);
|
||||
}
|
||||
|
||||
static void test_prev()
|
||||
{
|
||||
struct queue *history_q = playlist_get_queue(PL_SYSTEM, "History");
|
||||
struct track *track = queue_at(history_q, 0);
|
||||
struct playlist *history = playlist_lookup(PL_SYSTEM, "History");
|
||||
struct track *track = playlist_at(history, 0);
|
||||
|
||||
state_count = 0;
|
||||
g_assert_cmpuint(audio_prev()->tr_track, ==, 2);
|
||||
g_assert(queue_at(history_q, 0) == track);
|
||||
g_assert(playlist_at(history, 0) == track);
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_PLAYING);
|
||||
g_assert_cmpuint(audio_cur_track()->tr_track, ==, 2);
|
||||
g_assert_cmpuint(state_count, ==, 1);
|
||||
g_assert_cmpuint(test_wait_state(), ==, 1);
|
||||
|
||||
g_assert_cmpuint(audio_prev()->tr_track, ==, 1);
|
||||
g_assert(queue_at(history_q, 0) == track);
|
||||
g_assert(playlist_at(history, 0) == track);
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_PLAYING);
|
||||
g_assert_cmpuint(audio_cur_track()->tr_track, ==, 1);
|
||||
g_assert_cmpuint(state_count, ==, 2);
|
||||
g_assert_cmpuint(test_wait_state(), ==, 2);
|
||||
|
||||
g_assert_true(audio_pause());
|
||||
g_assert_cmpuint(audio_prev()->tr_track, ==, track_get(0)->tr_track);
|
||||
g_assert(queue_at(history_q, 0) == track);
|
||||
g_assert(playlist_at(history, 0) == track);
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_PLAYING);
|
||||
g_assert_cmpuint(audio_cur_track()->tr_track, ==, track_get(0)->tr_track);
|
||||
g_assert_cmpuint(state_count, ==, 4);
|
||||
g_assert_cmpuint(test_wait_state(), ==, 4);
|
||||
|
||||
g_assert_cmpuint(audio_prev()->tr_track, ==, track_get(1)->tr_track);
|
||||
g_assert(queue_at(history_q, 0) == track);
|
||||
g_assert(playlist_at(history, 0) == track);
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_PLAYING);
|
||||
g_assert_cmpuint(audio_cur_track()->tr_track, ==, track_get(1)->tr_track);
|
||||
g_assert_cmpuint(state_count, ==, 5);
|
||||
g_assert_cmpuint(test_wait_state(), ==, 5);
|
||||
|
||||
g_assert_cmpuint(audio_prev()->tr_track, ==, track_get(2)->tr_track);
|
||||
g_assert(queue_at(history_q, 0) == track);
|
||||
g_assert(playlist_at(history, 0) == track);
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_PLAYING);
|
||||
g_assert_cmpuint(audio_cur_track()->tr_track, ==, track_get(2)->tr_track);
|
||||
g_assert_cmpuint(state_count, ==, 6);
|
||||
g_assert_cmpuint(test_wait_state(), ==, 6);
|
||||
}
|
||||
|
||||
void test_autopause()
|
||||
{
|
||||
struct queue *history_q = playlist_get_queue(PL_SYSTEM, "History");
|
||||
struct playlist *history = playlist_lookup(PL_SYSTEM, "History");
|
||||
int i;
|
||||
|
||||
audio_pause_after(3);
|
||||
g_assert_cmpuint(pause_count, ==, 3);
|
||||
g_assert_true(audio_pause_after(3));
|
||||
g_assert_cmpint(pause_count, ==, 3);
|
||||
g_assert_cmpint(audio_get_pause_count(), ==, 3);
|
||||
|
||||
g_assert_false(audio_pause_after(-2));
|
||||
g_assert_cmpint(pause_count, ==, 3);
|
||||
g_assert_cmpint(audio_get_pause_count(), ==, 3);
|
||||
|
||||
pause_count = 0;
|
||||
audio_pause_after(3);
|
||||
g_assert_cmpuint(pause_count, ==, 0);
|
||||
g_assert_false(audio_pause_after(3));
|
||||
g_assert_cmpint(pause_count, ==, 0);
|
||||
g_assert_cmpint(audio_get_pause_count(), ==, 3);
|
||||
|
||||
audio_pause_after(5);
|
||||
g_assert_cmpuint(pause_count, ==, 5);
|
||||
g_assert_true(audio_pause_after(-1));
|
||||
g_assert_cmpint(pause_count, ==, -1);
|
||||
g_assert_cmpint(audio_get_pause_count(), ==, -1);
|
||||
|
||||
g_assert_true(audio_pause_after(5));
|
||||
g_assert_cmpint(pause_count, ==, 5);
|
||||
g_assert_cmpint(audio_get_pause_count(), ==, 5);
|
||||
|
||||
state_count = 0;
|
||||
for (i = 4; i > -1; i--) {
|
||||
audio_eos();
|
||||
g_assert_cmpuint(pause_count, ==, i);
|
||||
test_audio_eos();
|
||||
g_assert_cmpint(pause_count, ==, i);
|
||||
g_assert_cmpint(audio_get_pause_count(), ==, i);
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_PLAYING);
|
||||
g_assert(queue_at(history_q, 0) == audio_cur_track());
|
||||
g_assert(playlist_at(history, 0) == audio_cur_track());
|
||||
}
|
||||
g_assert_cmpuint(state_count, ==, 5);
|
||||
g_assert_cmpuint(test_wait_state(), ==, 5);
|
||||
|
||||
audio_eos();
|
||||
test_audio_eos();
|
||||
while (idle_run_task()) {}
|
||||
g_assert_cmpint(pause_count, ==, -1);
|
||||
g_assert_cmpint(audio_get_pause_count(), ==, -1);
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_PAUSED);
|
||||
g_assert_cmpuint(state_count, ==, 6);
|
||||
g_assert_cmpuint(test_wait_state(), ==, 6);
|
||||
|
||||
test_send_error();
|
||||
g_assert_cmpuint(audio_cur_state(), ==, GST_STATE_PAUSED);
|
||||
}
|
||||
|
||||
void test_filepath()
|
||||
{
|
||||
struct playlist *history = playlist_lookup(PL_SYSTEM, "History");
|
||||
const gchar *path = "tests/Music/Hyrule Symphony/01 - Title Theme.ogg";
|
||||
const gchar *path2 = "tests/Music/Ocarina of Time/01 - Title Theme.ogg";
|
||||
struct track *track = track_lookup(path);
|
||||
|
||||
load_count = 0;
|
||||
playlist_generic_clear(history);
|
||||
|
||||
g_assert_false(audio_load_filepath(NULL));
|
||||
g_assert_false(audio_load_filepath("tests/Music/00 - No Track.ogg"));
|
||||
g_assert_cmpuint(playlist_size(history), ==, 0);
|
||||
g_assert_cmpuint(load_count, ==, 0);
|
||||
|
||||
g_assert_true(audio_load_filepath(path));
|
||||
g_assert_cmpuint(load_count, ==, 1);
|
||||
g_assert(audio_cur_track() == track);
|
||||
g_assert_cmpuint(playlist_size(history), ==, 1);
|
||||
g_assert_true(playlist_has(history, audio_cur_track()));
|
||||
|
||||
g_assert_true(audio_load_filepath(path2));
|
||||
g_assert_cmpuint(load_count, ==, 2);
|
||||
g_assert(audio_cur_track() != track);
|
||||
g_assert_null(audio_cur_track()->tr_library);
|
||||
g_assert_cmpuint(playlist_size(history), ==, 1);
|
||||
g_assert_false(playlist_has(history, audio_cur_track()));
|
||||
|
||||
g_assert_true(audio_load_filepath(path));
|
||||
g_assert_cmpuint(load_count, ==, 3);
|
||||
g_assert(audio_cur_track() == track);
|
||||
g_assert_cmpuint(playlist_size(history), ==, 2);
|
||||
g_assert_true(playlist_has(history, audio_cur_track()));
|
||||
}
|
||||
|
||||
static void test_deinit()
|
||||
{
|
||||
core_deinit();
|
||||
test_loop_deinit();
|
||||
g_assert_null(audio_cur_track());
|
||||
g_assert_null(test_audio_player());
|
||||
g_assert_null(test_audio_pipeline());
|
||||
}
|
||||
|
||||
|
||||
|
@ -255,6 +296,7 @@ int main(int argc, char **argv)
|
|||
g_test_add_func("/Core/Audio/Next", test_next);
|
||||
g_test_add_func("/Core/Audio/Previous", test_prev);
|
||||
g_test_add_func("/Core/Audio/Automatic Pausing", test_autopause);
|
||||
g_test_add_func("/Core/Audio/Filepath", test_filepath);
|
||||
g_test_add_func("/Core/Audio/Deinitialization", test_deinit);
|
||||
return g_test_run();
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ static struct int_entry *__int_alloc(unsigned int val)
|
|||
return ent;
|
||||
}
|
||||
|
||||
static struct db_entry *int_alloc(const gchar *key)
|
||||
static struct db_entry *int_alloc(const gchar *key, unsigned int index)
|
||||
{
|
||||
unsigned int val;
|
||||
sscanf(key, "%u", &val);
|
||||
|
@ -44,11 +44,9 @@ static gchar *int_key(struct db_entry *dbe)
|
|||
return g_strdup_printf("%u", INT_ENTRY(dbe)->ie_val);
|
||||
}
|
||||
|
||||
static struct db_entry *int_read(struct file *f)
|
||||
static struct db_entry *int_read(struct file *f, unsigned int index)
|
||||
{
|
||||
unsigned int val;
|
||||
file_readf(f, "%u", &val);
|
||||
return &__int_alloc(val)->ie_dbe;
|
||||
return &__int_alloc(file_readu(f))->ie_dbe;
|
||||
}
|
||||
|
||||
static void int_write(struct file *file, struct db_entry *dbe)
|
||||
|
@ -67,16 +65,15 @@ static const struct db_ops int_ops = {
|
|||
|
||||
static void test_db_entry()
|
||||
{
|
||||
struct file f = FILE_INIT_DATA("", "test_db_entry", 0);
|
||||
struct int_entry *ent;
|
||||
struct file f;
|
||||
|
||||
ent = INT_ENTRY(int_ops.dbe_alloc("1"));
|
||||
ent = INT_ENTRY(int_ops.dbe_alloc("1", 0));
|
||||
g_assert_cmpuint(ent->ie_dbe.dbe_index, ==, 0);
|
||||
g_assert(ent->ie_dbe.dbe_data == ent);
|
||||
g_assert_cmpuint(ent->ie_val, ==, 1);
|
||||
g_assert_cmpstr_free(int_ops.dbe_key(&ent->ie_dbe), ==, "1");
|
||||
|
||||
file_init(&f, "test_db_entry", 0);
|
||||
file_open(&f, OPEN_WRITE);
|
||||
int_ops.dbe_write(&f, &ent->ie_dbe);
|
||||
file_close(&f);
|
||||
|
@ -85,7 +82,7 @@ static void test_db_entry()
|
|||
g_assert_cmpuint(test_free_count, ==, 1);
|
||||
|
||||
file_open(&f, OPEN_READ);
|
||||
ent = INT_ENTRY(int_ops.dbe_read(&f));
|
||||
ent = INT_ENTRY(int_ops.dbe_read(&f, 0));
|
||||
file_close(&f);
|
||||
|
||||
g_assert(ent->ie_dbe.dbe_data == ent);
|
||||
|
|
|
@ -16,7 +16,7 @@ void test_date()
|
|||
.d_month = 0,
|
||||
.d_day = 0,
|
||||
};
|
||||
struct file f = FILE_INIT("date", 0);
|
||||
struct file f = FILE_INIT_DATA("", "date", 0);
|
||||
|
||||
date_today(NULL);
|
||||
date_set(NULL, 0, 0, 0);
|
||||
|
|
|
@ -11,7 +11,7 @@ static void test_verify_constructor(struct file *file, gchar *fpath, gchar *ftmp
|
|||
{
|
||||
g_assert_null(file->f_file);
|
||||
g_assert_cmpuint(file_version(file), ==, OCARINA_MINOR_VERSION);
|
||||
g_assert_cmpuint(file->f_mode, ==, OPEN_READ);
|
||||
g_assert_cmpuint(file->f_mode, ==, CLOSED);
|
||||
g_assert_cmpstr_free(file_path(file), ==, fpath);
|
||||
g_assert_cmpstr_free(file_write_path(file), ==, ftmp);
|
||||
}
|
||||
|
@ -20,21 +20,21 @@ static void test_invalid_file(gconstpointer path)
|
|||
{
|
||||
struct file file;
|
||||
|
||||
file_init(&file, (gchar *)path, 0);
|
||||
file_init_data(&file, "", (gchar *)path, 0);
|
||||
test_verify_constructor(&file, "", "");
|
||||
|
||||
g_assert_false(file_open(&file, OPEN_READ));
|
||||
g_assert_null(file.f_file);
|
||||
g_assert_false(file_open(&file, OPEN_WRITE));
|
||||
g_assert_null(file.f_file);
|
||||
g_assert_cmpuint(file.f_mode, ==, OPEN_READ);
|
||||
g_assert_cmpuint(file.f_mode, ==, CLOSED);
|
||||
|
||||
g_assert_false(file_exists(&file));
|
||||
}
|
||||
|
||||
static void __test_file_subprocess()
|
||||
{
|
||||
struct file file = FILE_INIT("file.txt", 0);
|
||||
struct file file = FILE_INIT_DATA("", "file.txt", 0);
|
||||
gchar *basepath, *filepath, *realpath;
|
||||
|
||||
basepath = g_strjoin("/", g_get_user_data_dir(), OCARINA_NAME, NULL);
|
||||
|
@ -45,7 +45,8 @@ static void __test_file_subprocess()
|
|||
g_assert_false(file_exists(&file));
|
||||
|
||||
g_assert_false(file_open(&file, OPEN_READ));
|
||||
g_assert_true(file_open(&file, OPEN_WRITE));
|
||||
g_assert_false(file_open(&file, CLOSED));
|
||||
g_assert_true( file_open(&file, OPEN_WRITE));
|
||||
g_assert_nonnull(file.f_file);
|
||||
g_assert_cmpuint(file.f_mode, ==, OPEN_WRITE);
|
||||
g_assert_false(file_open(&file, OPEN_WRITE));
|
||||
|
@ -53,7 +54,7 @@ static void __test_file_subprocess()
|
|||
g_assert_false(file_exists(&file));
|
||||
file_close(&file);
|
||||
g_assert_null(file.f_file);
|
||||
g_assert_cmpuint(file.f_mode, ==, OPEN_WRITE);
|
||||
g_assert_cmpuint(file.f_mode, ==, CLOSED);
|
||||
g_assert_true(file_exists(&file));
|
||||
|
||||
g_chmod(filepath, 0444);
|
||||
|
@ -61,6 +62,7 @@ static void __test_file_subprocess()
|
|||
g_chmod(filepath, 0200);
|
||||
g_assert_false(file_open(&file, OPEN_READ));
|
||||
g_chmod(filepath, 0644);
|
||||
g_assert_false(file_open(&file, CLOSED));
|
||||
|
||||
g_assert_true(file_open(&file, OPEN_READ));
|
||||
g_assert_nonnull(file.f_file);
|
||||
|
@ -70,6 +72,7 @@ static void __test_file_subprocess()
|
|||
g_assert_false(file_remove(&file));
|
||||
g_assert_true(file_exists(&file));
|
||||
file_close(&file);
|
||||
g_assert_cmpuint(file.f_mode, ==, CLOSED);
|
||||
g_assert_true(file_remove(&file));
|
||||
g_assert_false(file_exists(&file));
|
||||
|
||||
|
@ -90,10 +93,8 @@ static void test_file()
|
|||
|
||||
static void test_io()
|
||||
{
|
||||
struct file fout = FILE_INIT("file.txt", 0);
|
||||
struct file fin = FILE_INIT("file.txt", 1);
|
||||
char *res = NULL;
|
||||
unsigned int i;
|
||||
struct file fout = FILE_INIT_DATA("", "file.txt", 0);
|
||||
struct file fin = FILE_INIT_DATA("", "file.txt", 1);
|
||||
|
||||
fout.f_version = 1;
|
||||
g_assert_true(file_open(&fout, OPEN_WRITE));
|
||||
|
@ -101,28 +102,28 @@ static void test_io()
|
|||
file_writef(&fout, "2 FGHIJ KLMNO\n");
|
||||
file_writef(&fout, "3 \n");
|
||||
file_writef(&fout, "4 5 PQRST\n");
|
||||
file_writef(&fout, "-6 UV WX YZ\n");
|
||||
file_close(&fout);
|
||||
g_assert_true(file_exists(&fout));
|
||||
|
||||
g_assert_true(file_open(&fin, OPEN_READ));
|
||||
g_assert_cmpuint(file_version(&fin), ==, 1);
|
||||
|
||||
g_assert_cmpuint(file_readf(&fin, "%u %ms\n", &i, &res), ==, 2);
|
||||
g_assert_cmpuint(i, ==, 1);
|
||||
g_assert_cmpstr_free(res, ==, "ABCDE");
|
||||
g_assert_cmpuint( file_readu(&fin), ==, 1);
|
||||
g_assert_cmpstr_free(file_readl(&fin), ==, "ABCDE");
|
||||
|
||||
g_assert_cmpuint(file_readf(&fin, "%u %m[^\n]\n", &i, &res), ==, 2);
|
||||
g_assert_cmpuint(i, ==, 2);
|
||||
g_assert_cmpstr_free(res, ==, "FGHIJ KLMNO");
|
||||
g_assert_cmpuint( file_readu(&fin), ==, 2);
|
||||
g_assert_cmpstr_free(file_readl(&fin), ==, "FGHIJ KLMNO");
|
||||
|
||||
g_assert_cmpuint(file_readf(&fin, "%u", &i), ==, 1);
|
||||
res = file_readl(&fin);
|
||||
g_assert_cmpuint(i, ==, 3);
|
||||
g_assert_cmpstr_free(res, ==, "");
|
||||
g_assert_cmpuint( file_readu(&fin), ==, 3);
|
||||
g_assert_cmpstr_free(file_readl(&fin), ==, "");
|
||||
|
||||
g_assert_cmpuint(file_readf(&fin, "%u %m[^\n]", &i, &res), ==, 2);
|
||||
g_assert_cmpuint(i, ==, 4);
|
||||
g_assert_cmpstr_free(res, ==, "5 PQRST");
|
||||
g_assert_cmpuint( file_readu(&fin), ==, 4);
|
||||
g_assert_cmpstr_free(file_readl(&fin), ==, "5 PQRST");
|
||||
|
||||
g_assert_cmpint( file_readd(&fin), ==, -6);
|
||||
g_assert_cmpstr_free(file_readw(&fin), ==, "UV");
|
||||
g_assert_cmpstr_free(file_readl(&fin), ==, "WX YZ");
|
||||
|
||||
file_close(&fin);
|
||||
g_assert_cmpuint(file_version(&fin), ==, OCARINA_MINOR_VERSION);
|
||||
|
@ -130,8 +131,8 @@ static void test_io()
|
|||
|
||||
static void __test_versioning_subprocess(unsigned int out, unsigned int in)
|
||||
{
|
||||
struct file fout = FILE_INIT("file.txt", out);
|
||||
struct file fin = FILE_INIT("file.txt", in);
|
||||
struct file fout = FILE_INIT_DATA("", "file.txt", out);
|
||||
struct file fin = FILE_INIT_DATA("", "file.txt", in);
|
||||
|
||||
fout.f_version = out;
|
||||
fin.f_version = in;
|
||||
|
@ -171,44 +172,67 @@ static void test_versioning_new()
|
|||
|
||||
static void test_cache()
|
||||
{
|
||||
struct cache_file file = CACHE_FILE_INIT("dir", "file.txt");
|
||||
struct cache_file copy = CACHE_FILE_INIT("dir", "copy.txt");
|
||||
gchar *basepath, *filepath, *writepath;
|
||||
struct file file, copy;
|
||||
|
||||
basepath = g_strjoin("/", g_get_user_cache_dir(), OCARINA_NAME, NULL);
|
||||
filepath = g_strjoin("/", basepath, "dir", "file.txt", NULL);
|
||||
writepath = g_strjoin("/", basepath, "dir", ".file.txt.tmp", NULL);
|
||||
file_init_cache(&file, "dir", "file.txt");
|
||||
file_init_cache(©, "dir", "copy.txt");
|
||||
|
||||
g_assert_null(file.cf_file);
|
||||
g_assert_cmpstr(file.cf_name, ==, "file.txt");
|
||||
g_assert_cmpstr(file.cf_subdir, ==, "dir");
|
||||
basepath = g_strjoin("/", g_get_user_cache_dir(), OCARINA_NAME, "dir", NULL);
|
||||
filepath = g_strjoin("/", basepath, "file.txt", NULL);
|
||||
writepath = g_strjoin("/", basepath, ".file.txt.tmp", NULL);
|
||||
|
||||
g_assert_cmpstr_free(cache_file_path(&file), ==, filepath);
|
||||
g_assert_cmpstr_free(cache_file_write_path(&file), ==, writepath);
|
||||
g_assert_null(file.f_file);
|
||||
g_assert_cmpstr(file.f_name, ==, "file.txt");
|
||||
g_assert_cmpstr(file.f_subdir, ==, "dir");
|
||||
g_assert_cmpuint(file.f_mode, ==, CLOSED);
|
||||
|
||||
g_assert_cmpstr_free(file_path(&file), ==, filepath);
|
||||
g_assert_cmpstr_free(file_write_path(&file), ==, writepath);
|
||||
|
||||
/* Test writing data to a cache file. */
|
||||
g_assert_false(cache_file_exists(&file));
|
||||
g_assert_false(cache_file_open(&file, OPEN_READ));
|
||||
g_assert_true(cache_file_open(&file, OPEN_WRITE));
|
||||
g_assert_nonnull(file.cf_file);
|
||||
g_assert_false(cache_file_open(&file, OPEN_WRITE));
|
||||
g_assert_false(file_exists(&file));
|
||||
g_assert_false(file_open(&file, OPEN_READ_BINARY));
|
||||
g_assert_false(file_open(&file, CLOSED));
|
||||
g_assert_true( file_open(&file, OPEN_WRITE_BINARY));
|
||||
g_assert_nonnull(file.f_file);
|
||||
g_assert_cmpuint(file.f_mode, ==, OPEN_WRITE_BINARY);
|
||||
g_assert_false(file_open(&file, OPEN_WRITE_BINARY));
|
||||
|
||||
g_assert_false(cache_file_exists(&file));
|
||||
g_assert_cmpuint(cache_file_write(&file, "abcde", 5), ==, 5);
|
||||
cache_file_close(&file);
|
||||
g_assert_null(file.cf_file);
|
||||
g_assert_true(cache_file_exists(&file));
|
||||
g_assert_false(file_exists(&file));
|
||||
g_assert_cmpuint(file_write(&file, "abcde", 5), ==, 5);
|
||||
file_close(&file);
|
||||
g_assert_null(file.f_file);
|
||||
g_assert_cmpuint(file.f_mode, ==, CLOSED);
|
||||
g_assert_true(file_exists(&file));
|
||||
|
||||
g_assert_true(file_open(&file, OPEN_READ_BINARY));
|
||||
g_assert_cmpuint(file.f_mode, ==, OPEN_READ_BINARY);
|
||||
g_assert_cmpuint(file.f_version, ==, OCARINA_MINOR_VERSION);
|
||||
g_assert_cmpuint(file.f_prev, ==, 0);
|
||||
g_assert_cmpstr_free(file_read(&file), ==, "abcde");
|
||||
file_close(&file);
|
||||
|
||||
/* Test importing a file into the cache. */
|
||||
g_assert_false(cache_file_exists(©));
|
||||
g_assert_false(cache_file_import(©, filepath));
|
||||
g_assert_false(cache_file_exists(©));
|
||||
g_assert_true(cache_file_open(©, OPEN_WRITE));
|
||||
g_assert_false(cache_file_import(©, NULL));
|
||||
g_assert_true(cache_file_import(©, filepath));
|
||||
g_assert_false(cache_file_exists(©));
|
||||
cache_file_close(©);
|
||||
g_assert_true(cache_file_exists(©));
|
||||
g_assert_false(file_exists(©));
|
||||
g_assert_false(file_import(©, filepath));
|
||||
g_assert_false(file_exists(©));
|
||||
g_assert_true( file_open(©, OPEN_WRITE_BINARY));
|
||||
g_assert_false(file_import(©, NULL));
|
||||
g_assert_true( file_import(©, filepath));
|
||||
g_assert_false(file_exists(©));
|
||||
file_close(©);
|
||||
g_assert_true(file_exists(©));
|
||||
|
||||
/* Test removing cache files. */
|
||||
g_assert_true(file_remove(©));
|
||||
g_assert_false(file_exists(©));
|
||||
g_assert_true(file_exists(&file));
|
||||
g_assert_true(g_file_test(basepath, G_FILE_TEST_EXISTS));
|
||||
|
||||
g_assert_true(file_remove(&file));
|
||||
g_assert_false(file_exists(&file));
|
||||
g_assert_false(g_file_test(basepath, G_FILE_TEST_EXISTS));
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
|
|
|
@ -19,7 +19,7 @@ static void test_idle(gconstpointer arg)
|
|||
unsigned int n = GPOINTER_TO_UINT(arg);
|
||||
cur = -1;
|
||||
|
||||
idle_init();
|
||||
idle_init(IDLE_ASYNC);
|
||||
|
||||
g_assert_cmpfloat(idle_progress(), ==, 1.0);
|
||||
g_assert_false(idle_run_task());
|
||||
|
|
|
@ -0,0 +1,456 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/idle.h>
|
||||
#include <core/playlist.h>
|
||||
#include <core/settings.h>
|
||||
#include <core/tags/tags.h>
|
||||
|
||||
static struct playlist *cb_playlist = NULL;
|
||||
static struct track *cb_track = NULL;
|
||||
|
||||
static void test_pl_alloc(struct playlist *playlist)
|
||||
{
|
||||
cb_playlist = NULL;
|
||||
}
|
||||
|
||||
static void test_pl_removed(struct playlist *playlist, struct track *track,
|
||||
unsigned int n)
|
||||
{
|
||||
cb_playlist = playlist;
|
||||
cb_track = track;
|
||||
}
|
||||
|
||||
static void test_pl_callback(struct playlist *playlist, struct track *track)
|
||||
{
|
||||
cb_playlist = playlist;
|
||||
cb_track = track;
|
||||
}
|
||||
|
||||
static struct playlist_ops test_noop;
|
||||
static struct playlist_ops test_ops = {
|
||||
.pl_add = playlist_generic_add,
|
||||
.pl_can_select = playlist_generic_can_select,
|
||||
.pl_remove = playlist_generic_remove,
|
||||
.pl_set_random = playlist_generic_set_random,
|
||||
.pl_sort = playlist_generic_sort,
|
||||
.pl_rearrange = playlist_generic_rearrange,
|
||||
};
|
||||
static struct playlist_callbacks test_cb = {
|
||||
.pl_cb_alloc = test_pl_alloc,
|
||||
.pl_cb_added = test_pl_callback,
|
||||
.pl_cb_removed = test_pl_removed,
|
||||
.pl_cb_updated = test_pl_callback,
|
||||
};
|
||||
|
||||
static void test_null()
|
||||
{
|
||||
g_assert_null(playlist_new(PL_MAX_TYPE, "NULL"));
|
||||
g_assert_null(playlist_new(PL_MAX_TYPE, NULL));
|
||||
g_assert_false(playlist_delete(NULL));
|
||||
playlist_generic_free(NULL);
|
||||
|
||||
playlist_generic_init(NULL, 0);
|
||||
playlist_generic_deinit(NULL);
|
||||
|
||||
g_assert_null(playlist_lookup(PL_MAX_TYPE, "NULL"));
|
||||
g_assert_null(playlist_lookup(PL_MAX_TYPE, NULL));
|
||||
g_assert_null(playlist_get(PL_MAX_TYPE, 0));
|
||||
|
||||
g_assert(playlist_current() == playlist_lookup(PL_SYSTEM, "Collection"));
|
||||
g_assert_false(playlist_select(NULL));
|
||||
g_assert(playlist_current() == playlist_lookup(PL_SYSTEM, "Collection"));
|
||||
g_assert_null(playlist_next());
|
||||
playlist_selected(NULL);
|
||||
playlist_played(NULL);
|
||||
|
||||
g_assert_false(playlist_add(NULL, NULL));
|
||||
g_assert_false(playlist_add(NULL, track_get(0)));
|
||||
g_assert_false(playlist_has(NULL, NULL));
|
||||
g_assert_false(playlist_has(NULL, track_get(0)));
|
||||
g_assert_cmpuint(playlist_size(NULL), ==, 0);
|
||||
g_assert_false(playlist_remove(NULL, NULL));
|
||||
g_assert_false(playlist_remove(NULL, track_get(0)));
|
||||
|
||||
playlist_set_random(NULL, true);
|
||||
g_assert_false(playlist_sort(NULL, COMPARE_TRACK));
|
||||
g_assert_false(playlist_sort(NULL, COMPARE_TRACK));
|
||||
playlist_generic_resort(NULL);
|
||||
playlist_clear_sort(NULL);
|
||||
g_assert_false(playlist_rearrange(NULL, 0, 0));
|
||||
|
||||
playlist_set_search(NULL, NULL);
|
||||
|
||||
g_assert_false(playlist_generic_add(NULL, NULL));
|
||||
g_assert_false(playlist_generic_add_front(NULL, NULL));
|
||||
g_assert_false(playlist_generic_remove(NULL, NULL));
|
||||
g_assert_null(playlist_at(NULL, 0));
|
||||
playlist_generic_update(NULL, NULL);
|
||||
playlist_generic_clear(NULL);
|
||||
|
||||
playlist_generic_save(NULL, NULL, PL_SAVE_ALL);
|
||||
playlist_generic_load(NULL, NULL, PL_SAVE_ALL);
|
||||
|
||||
g_assert_null(playlist_iter_get(NULL, 0));
|
||||
g_assert_false(playlist_iter_next(NULL));
|
||||
g_assert_null(playlist_iter_track(NULL));
|
||||
g_assert_cmpint(playlist_iter_index(NULL, NULL), ==, -1);
|
||||
|
||||
g_assert_false(playlist_current_set(NULL, 0));
|
||||
g_assert_false(playlist_current_next(NULL));
|
||||
g_assert_false(playlist_current_previous(NULL));
|
||||
g_assert_null(playlist_current_track(NULL));
|
||||
g_assert_cmpint(playlist_current_index(NULL), ==, -1);
|
||||
}
|
||||
|
||||
static void test_playlist()
|
||||
{
|
||||
struct playlist p = DEFINE_PLAYLIST(PL_MAX_TYPE, "Test", 0, &test_ops);
|
||||
unsigned int ex_length = 0;
|
||||
playlist_iter it;
|
||||
int i;
|
||||
|
||||
g_assert_cmpuint(playlist_size(&p), ==, 0);
|
||||
playlist_generic_init(&p, 0);
|
||||
g_assert_cmpuint(playlist_size(&p), ==, 0);
|
||||
g_assert_cmpuint(p.pl_length, ==, 0);
|
||||
g_assert_cmpuint(g_slist_length(p.pl_sort), ==, 0);
|
||||
|
||||
for (i = 0; i < 13; i++) {
|
||||
ex_length += track_get(i)->tr_length;
|
||||
playlist_generic_add_front(&p, track_get(i));
|
||||
g_assert_true(playlist_has(&p, track_get(i)));
|
||||
g_assert_cmpuint(p.pl_length, ==, ex_length);
|
||||
g_assert_cmpuint(playlist_size(&p), ==, i + 1);
|
||||
g_assert(cb_playlist == &p);
|
||||
g_assert(cb_track == track_get(i));
|
||||
}
|
||||
g_assert_false(playlist_generic_add_front(&p, NULL));
|
||||
|
||||
/* Trigger an update for each track. */
|
||||
i = 13;
|
||||
playlist_for_each(&p, it) {
|
||||
g_assert_cmpuint(playlist_iter_track(it)->tr_track, ==, i--);
|
||||
playlist_generic_update(&p, playlist_iter_track(it));
|
||||
g_assert(cb_playlist == &p);
|
||||
g_assert(cb_track == playlist_iter_track(it));
|
||||
}
|
||||
playlist_generic_update(&p, NULL);
|
||||
g_assert(cb_playlist == &p);
|
||||
g_assert_null(cb_track);
|
||||
|
||||
/* Remove all tracks. */
|
||||
playlist_current_set(&p, 12);
|
||||
for (i = 0; i < 13; i++) {
|
||||
ex_length -= track_get(i)->tr_length;
|
||||
g_assert_true(playlist_remove(&p, track_get(i)));
|
||||
g_assert_false(playlist_has(&p, track_get(i)));
|
||||
g_assert(cb_playlist == &p);
|
||||
g_assert(cb_track == track_get(i));
|
||||
g_assert_cmpuint(p.pl_length, ==, ex_length);
|
||||
g_assert_cmpint(playlist_current_index(&p), ==, (11 - i));
|
||||
g_assert_cmpuint(playlist_size(&p), ==, (12 - i));
|
||||
if (i < 12)
|
||||
g_assert_nonnull(p.pl_current);
|
||||
}
|
||||
g_assert_false(playlist_generic_remove(&p, NULL));
|
||||
g_assert_false(playlist_has(&p, track_get(i)));
|
||||
g_assert(cb_playlist == &p);
|
||||
g_assert(cb_track == track_get(12));
|
||||
g_assert_cmpuint(p.pl_length, ==, ex_length);
|
||||
g_assert_null(p.pl_current);
|
||||
|
||||
/* Re-add the tracks! */
|
||||
for (i = 0; i < 13; i++) {
|
||||
ex_length += track_get(i)->tr_length;
|
||||
g_assert_true( playlist_add(&p, track_get(i)));
|
||||
g_assert_false(playlist_add(&p, track_get(i)));
|
||||
g_assert_true(playlist_has(&p, track_get(i)));
|
||||
g_assert(playlist_at(&p, i) == track_get(i));
|
||||
g_assert_cmpuint(p.pl_length, ==, ex_length);
|
||||
g_assert_cmpuint(playlist_size(&p), ==, i + 1);
|
||||
g_assert(cb_playlist == &p);
|
||||
g_assert(cb_track == track_get(i));
|
||||
}
|
||||
g_assert_false(playlist_generic_add(&p, NULL));
|
||||
g_assert_cmpuint(p.pl_length, ==, ex_length);
|
||||
|
||||
/* Now clear the playlist. */
|
||||
playlist_current_set(&p, 12);
|
||||
playlist_generic_clear(&p);
|
||||
g_assert_cmpuint(playlist_size(&p), ==, 0);
|
||||
g_assert_cmpuint(p.pl_length, ==, 0);
|
||||
g_assert_null(p.pl_current);
|
||||
|
||||
/* Set search text. */
|
||||
playlist_set_search(&p, "test");
|
||||
g_assert_cmpstr(p.pl_search[0], ==, "test");
|
||||
playlist_set_search(&p, "Test Text");
|
||||
g_assert_cmpstr(p.pl_search[0], ==, "test");
|
||||
g_assert_cmpstr(p.pl_search[1], ==, "text");
|
||||
|
||||
/* Deinitialize the playlist. */
|
||||
playlist_generic_deinit(&p);
|
||||
g_assert_cmpuint(playlist_size(&p), ==, 0);
|
||||
g_assert_cmpuint(p.pl_length, ==, 0);
|
||||
g_assert_null(p.pl_current);
|
||||
g_assert_null(p.pl_search);
|
||||
g_assert_null(p.pl_sort);
|
||||
}
|
||||
|
||||
static void test_sorting()
|
||||
{
|
||||
struct playlist p = DEFINE_PLAYLIST(PL_MAX_TYPE, "Test", 0, &test_ops);
|
||||
struct track *track;
|
||||
unsigned int i;
|
||||
|
||||
playlist_generic_init(&p, 4, COMPARE_ARTIST, COMPARE_YEAR,
|
||||
COMPARE_ALBUM, COMPARE_TRACK);
|
||||
g_assert_cmpuint(g_slist_length(p.pl_sort), ==, 4);
|
||||
playlist_clear_sort(&p);
|
||||
g_assert_cmpuint(g_slist_length(p.pl_sort), ==, 0);
|
||||
|
||||
for (i = 0; i < 13; i++) {
|
||||
track = track_get(i);
|
||||
track->tr_count = (track->tr_track % 2) ? 4 : 2;
|
||||
playlist_generic_add_front(&p, track);
|
||||
}
|
||||
|
||||
cb_playlist = NULL;
|
||||
cb_track = NULL;
|
||||
p.pl_ops = &test_noop;
|
||||
g_assert_false(playlist_sort(&p, COMPARE_TRACK));
|
||||
g_assert_null(cb_playlist);
|
||||
g_assert_null(cb_track);
|
||||
p.pl_ops = &test_ops;
|
||||
|
||||
g_assert_true(playlist_sort(&p, COMPARE_TRACK));
|
||||
g_assert(cb_playlist == &p);
|
||||
g_assert_null(cb_track);
|
||||
for (i = 0; i < 13; i++) {
|
||||
track = playlist_at(&p, i);
|
||||
g_assert_cmpuint(track->tr_track, ==, i + 1);
|
||||
}
|
||||
|
||||
playlist_clear_sort(&p);
|
||||
playlist_generic_resort(&p);
|
||||
|
||||
g_assert_true(playlist_sort(&p, COMPARE_COUNT));
|
||||
for (i = 0; i < 13; i++) {
|
||||
track = playlist_at(&p, i);
|
||||
g_assert_cmpuint(track->tr_count, ==, (i < 6) ? 2 : 4);
|
||||
}
|
||||
|
||||
g_assert_true(playlist_sort(&p, COMPARE_TRACK));
|
||||
for (i = 0; i < 13; i++) {
|
||||
track = playlist_at(&p, i);
|
||||
g_assert_cmpuint(track->tr_count, ==, (i < 6) ? 2 : 4);
|
||||
if (i < 6)
|
||||
g_assert_cmpuint(track->tr_track, ==, (i + 1) * 2);
|
||||
else
|
||||
g_assert_cmpuint(track->tr_track, ==, (2 * i) - 11);
|
||||
}
|
||||
|
||||
g_assert_true(playlist_sort(&p, COMPARE_COUNT));
|
||||
for (i = 0; i < 13; i++) {
|
||||
track = playlist_at(&p, i);
|
||||
g_assert_cmpuint(track->tr_count, ==, (i < 7) ? 4 : 2);
|
||||
if (i < 7)
|
||||
g_assert_cmpuint(track->tr_track, ==, (2 * i) + 1);
|
||||
else
|
||||
g_assert_cmpuint(track->tr_track, ==, (2 * i) - 12);
|
||||
}
|
||||
|
||||
/* Test inserting at a sorted position. */
|
||||
playlist_generic_clear(&p);
|
||||
playlist_clear_sort(&p);
|
||||
playlist_sort(&p, COMPARE_TRACK);
|
||||
playlist_sort(&p, COMPARE_TRACK);
|
||||
for (i = 0; i < 13; i++) {
|
||||
g_assert_false(playlist_has(&p, track_get(i)));
|
||||
g_assert_true( playlist_add(&p, track_get(i)));
|
||||
g_assert_false(playlist_add(&p, track_get(i)));
|
||||
}
|
||||
for (i = 0; i < 13; i++)
|
||||
g_assert_cmpuint(playlist_at(&p, i)->tr_track, ==, 13 - i);
|
||||
|
||||
/* Deinitialize the playlist. */
|
||||
playlist_generic_deinit(&p);
|
||||
g_assert_cmpuint(playlist_size(&p), ==, 0);
|
||||
g_assert_cmpuint(p.pl_length, ==, 0);
|
||||
g_assert_null(p.pl_current);
|
||||
g_assert_null(p.pl_sort);
|
||||
}
|
||||
|
||||
static void test_rearranging()
|
||||
{
|
||||
struct playlist p = DEFINE_PLAYLIST(PL_MAX_TYPE, "Test", 0, &test_ops);
|
||||
struct track *track;
|
||||
unsigned int i;
|
||||
|
||||
playlist_generic_init(&p, 4, COMPARE_ARTIST, COMPARE_YEAR,
|
||||
COMPARE_ALBUM, COMPARE_TRACK);
|
||||
g_assert_cmpuint(g_slist_length(p.pl_sort), ==, 4);
|
||||
|
||||
for (i = 0; i < 13; i++)
|
||||
playlist_add(&p, track_get(i));
|
||||
|
||||
g_assert_false(playlist_rearrange(&p, 42, 4));
|
||||
g_assert_cmpuint(g_slist_length(p.pl_sort), ==, 4);
|
||||
g_assert_false(playlist_rearrange(&p, 4, 42));
|
||||
g_assert_cmpuint(g_slist_length(p.pl_sort), ==, 4);
|
||||
g_assert_false(playlist_rearrange(&p, 4, 4));
|
||||
g_assert_cmpuint(g_slist_length(p.pl_sort), ==, 4);
|
||||
g_assert_true(playlist_rearrange(&p, 12, 0));
|
||||
g_assert_cmpuint(g_slist_length(p.pl_sort), ==, 0);
|
||||
g_assert_true(playlist_rearrange(&p, 1, 12));
|
||||
g_assert_cmpuint(g_slist_length(p.pl_sort), ==, 0);
|
||||
|
||||
for (i = 0; i < 13; i++) {
|
||||
track = playlist_at(&p, i);
|
||||
if (i == 0)
|
||||
g_assert_cmpuint(track->tr_track, ==, 13);
|
||||
else if (i == 12)
|
||||
g_assert_cmpuint(track->tr_track, ==, 1);
|
||||
else
|
||||
g_assert_cmpuint(track->tr_track, ==, i + 1);
|
||||
}
|
||||
|
||||
playlist_generic_deinit(&p);
|
||||
}
|
||||
|
||||
static void test_next()
|
||||
{
|
||||
struct playlist p = DEFINE_PLAYLIST(PL_MAX_TYPE, "Test", 0, &test_ops);
|
||||
struct track *track;
|
||||
unsigned int i;
|
||||
|
||||
g_random_set_seed(0);
|
||||
playlist_generic_init(&p, 0);
|
||||
|
||||
for (i = 0; i < 13; i++)
|
||||
playlist_generic_add(&p, track_get(i));
|
||||
|
||||
g_assert_true(playlist_select(&p));
|
||||
g_assert(playlist_current() == &p);
|
||||
|
||||
/* Test playlist_next() with wraparound, but no random. */
|
||||
for (i = 0; i < (13 * 2); i++) {
|
||||
track = playlist_next();
|
||||
g_assert_nonnull(track);
|
||||
g_assert_cmpuint(track->tr_track, ==, (i % 13) + 1);
|
||||
g_assert_cmpuint(playlist_size(&p), ==, 13);
|
||||
}
|
||||
|
||||
playlist_set_random(&p, true);
|
||||
g_assert_true(p.pl_random);
|
||||
|
||||
/* rand() = { 10, 4, 6, 1, 8, 4, 1 } */
|
||||
g_assert_cmpuint(playlist_next()->tr_track, ==, 9);
|
||||
g_assert_cmpuint(playlist_next()->tr_track, ==, 13);
|
||||
g_assert_cmpuint(playlist_next()->tr_track, ==, 6);
|
||||
g_assert_cmpuint(playlist_next()->tr_track, ==, 7);
|
||||
g_assert_cmpuint(playlist_next()->tr_track, ==, 2);
|
||||
g_assert_cmpuint(playlist_next()->tr_track, ==, 6);
|
||||
g_assert_cmpuint(playlist_next()->tr_track, ==, 1);
|
||||
g_assert_cmpuint(playlist_size(&p), ==, 13);
|
||||
|
||||
/* Deinitialize the playlist. */
|
||||
playlist_generic_deinit(&p);
|
||||
g_assert_cmpuint(playlist_size(&p), ==, 0);
|
||||
g_assert_cmpuint(p.pl_length, ==, 0);
|
||||
g_assert_null(p.pl_current);
|
||||
g_assert_null(p.pl_sort);
|
||||
}
|
||||
|
||||
static void test_save_load()
|
||||
{
|
||||
struct playlist p = DEFINE_PLAYLIST(PL_MAX_TYPE, "Test", 0, &test_ops);
|
||||
struct playlist q = DEFINE_PLAYLIST(PL_MAX_TYPE, "Test 2", 0, NULL);
|
||||
struct playlist r = DEFINE_PLAYLIST(PL_MAX_TYPE, "Test", 0, &test_ops);
|
||||
struct playlist s = DEFINE_PLAYLIST(PL_MAX_TYPE, "Test 2", 0, NULL);
|
||||
struct file f = FILE_INIT_DATA("", "test.playlist", 0);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < 13; i++) {
|
||||
playlist_generic_add(&p, track_get(i));
|
||||
playlist_generic_add(&q, track_get(i));
|
||||
playlist_generic_add(&r, track_get(i));
|
||||
}
|
||||
playlist_set_random(&p, true);
|
||||
playlist_clear_sort(&p);
|
||||
playlist_sort(&p, COMPARE_TRACK);
|
||||
playlist_current_set(&p, 3);
|
||||
playlist_current_set(&q, 4);
|
||||
|
||||
g_assert_false(file_exists(&f));
|
||||
g_assert_true( file_open(&f, OPEN_WRITE));
|
||||
playlist_generic_save(&p, &f, PL_SAVE_METADATA);
|
||||
playlist_generic_save(&q, &f, PL_SAVE_ALL);
|
||||
file_close(&f);
|
||||
|
||||
g_assert_true(file_open(&f, OPEN_READ));
|
||||
playlist_generic_load(&r, &f, PL_SAVE_METADATA);
|
||||
playlist_generic_load(&s, &f, PL_SAVE_ALL);
|
||||
file_close(&f);
|
||||
|
||||
g_assert_true(r.pl_random);
|
||||
g_assert_cmpuint(playlist_current_index(&r), ==, 3);
|
||||
g_assert_cmpuint(g_slist_length(r.pl_sort), ==, 1);
|
||||
g_assert_cmpuint(GPOINTER_TO_UINT(r.pl_sort->data), ==, COMPARE_TRACK);
|
||||
|
||||
g_assert_false(s.pl_random);
|
||||
g_assert_cmpuint(playlist_current_index(&s), ==, 4);
|
||||
g_assert(playlist_current_track(&s) == playlist_at(&q, 4));
|
||||
g_assert_cmpuint(g_slist_length(s.pl_sort), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(&s), ==, 13);
|
||||
|
||||
/* Deinitialize the playlist. */
|
||||
playlist_generic_deinit(&p);
|
||||
g_assert_cmpuint(playlist_size(&p), ==, 0);
|
||||
g_assert_cmpuint(p.pl_length, ==, 0);
|
||||
g_assert_null(p.pl_current);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct library *library;
|
||||
int ret;
|
||||
|
||||
idle_init(IDLE_SYNC);
|
||||
settings_init();
|
||||
tags_init();
|
||||
playlist_init(&test_cb);
|
||||
while (idle_run_task()) {};
|
||||
|
||||
library = library_find("tests/Music");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/01 - Title Theme.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/02 - Kokiri Forest.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/03 - Hyrule Field.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/04 - Hyrule Castle.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/05 - Lon Lon Ranch.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/06 - Kakariko Village.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/07 - Death Mountain.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/08 - Zora's Domain.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/09 - Gerudo Valley.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/10 - Ganondorf.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/11 - Princess Zelda.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/12 - Ocarina Medley.ogg");
|
||||
track_add(library,
|
||||
"tests/Music/Hyrule Symphony/13 - The Legend of Zelda Medley.ogg");
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/Core/Playlist/NULL", test_null);
|
||||
g_test_add_func("/Core/Playlists/General", test_playlist);
|
||||
g_test_add_func("/Core/Playlists/Sorting", test_sorting);
|
||||
g_test_add_func("/Core/Playlists/Rearranging", test_rearranging);
|
||||
g_test_add_func("/Core/Playlists/Next Track", test_next);
|
||||
g_test_add_func("/Core/Playlist/Save and Load", test_save_load);
|
||||
ret = g_test_run();
|
||||
|
||||
playlist_deinit();
|
||||
tags_deinit();
|
||||
settings_deinit();
|
||||
idle_deinit();
|
||||
return ret;
|
||||
}
|
|
@ -12,34 +12,47 @@
|
|||
|
||||
void test_artist()
|
||||
{
|
||||
struct artist *artist;
|
||||
struct playlist *playlist;
|
||||
struct artist *artist;
|
||||
|
||||
g_assert_false(playlist_new(PL_ARTIST, "Koji Kondo"));
|
||||
g_assert_null(playlist_get_queue(PL_ARTIST, "Koji Kondo"));
|
||||
g_assert_null(playlist_new(PL_ARTIST, "Koji Kondo"));
|
||||
g_assert_null(playlist_lookup(PL_ARTIST, "Koji Kondo"));
|
||||
|
||||
artist = artist_find("Koji Kondo");
|
||||
artist = artist_find("Koji Kondo");
|
||||
g_assert_null(artist->ar_playlist);
|
||||
g_assert_false(playlist_add(PL_ARTIST, "Koji Kondo", track_get(0)));
|
||||
g_assert_false(playlist_select(PL_ARTIST, "Koji Kondo"));
|
||||
g_assert_false(playlist_select(PL_ARTIST, "Hajime Wakai"));
|
||||
g_assert_false(playlist_add(NULL, track_get(0)));
|
||||
g_assert_false(playlist_select(NULL));
|
||||
|
||||
pl_artist_deinit();
|
||||
pl_artist_init(NULL);
|
||||
pl_artist_init();
|
||||
|
||||
g_assert_cmpuint(playlist_get_id(PL_ARTIST, "Koji Kondo"), ==, 0);
|
||||
g_assert_cmpstr_free(playlist_get_name(PL_ARTIST, 0), ==, "Koji Kondo");
|
||||
while (idle_run_task()) {};
|
||||
|
||||
g_assert_cmpuint(playlist_size(PL_ARTIST, "Koji Kondo"), ==, 2);
|
||||
g_assert_nonnull(artist->ar_playlist);
|
||||
g_assert_false(playlist_remove(PL_ARTIST, "Koji Kondo", track_get(0)));
|
||||
g_assert_cmpuint(playlist_size(PL_ARTIST, "Koji Kondo"), ==, 2);
|
||||
playlist = playlist_lookup(PL_ARTIST, "Koji Kondo");
|
||||
g_assert_nonnull(playlist);
|
||||
|
||||
g_assert_true(playlist_select(PL_ARTIST, "Koji Kondo"));
|
||||
g_assert_cmpuint(playlist->pl_id, ==, artist_index(artist));
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 2);
|
||||
g_assert_nonnull(artist->ar_playlist);
|
||||
g_assert_false(playlist_remove(playlist, track_get(0)));
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 2);
|
||||
|
||||
g_assert(playlist_current() != playlist);
|
||||
g_assert_true(playlist_select(playlist));
|
||||
g_assert_cmpuint(settings_get("core.playlist.cur.type"), ==, PL_ARTIST);
|
||||
g_assert_cmpuint(settings_get("core.playlist.cur.id"), ==, 0);
|
||||
g_assert(playlist_current() == playlist);
|
||||
g_assert_false(playlist_select(playlist));
|
||||
|
||||
g_assert_false(playlist_delete(PL_ARTIST, "Koji Kondo"));
|
||||
playlist_selected(track_get(0));
|
||||
playlist_played(track_get(0));
|
||||
|
||||
g_assert_cmpuint(playlist_next()->tr_track, ==, 1);
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 2);
|
||||
g_assert_true(playlist_has(playlist, track_get(0)));
|
||||
g_assert_true(playlist_has(playlist, track_get(1)));
|
||||
|
||||
g_assert_false(playlist_delete(playlist));
|
||||
pl_artist_deinit();
|
||||
g_assert_null(artist->ar_playlist);
|
||||
}
|
||||
|
@ -49,7 +62,7 @@ int main(int argc, char **argv)
|
|||
struct library *library;
|
||||
int ret;
|
||||
|
||||
idle_init_sync();
|
||||
idle_init(IDLE_SYNC);
|
||||
settings_init();
|
||||
tags_init();
|
||||
playlist_init(NULL);
|
||||
|
|
|
@ -13,88 +13,98 @@ void test_library()
|
|||
struct playlist *playlist;
|
||||
struct library *library;
|
||||
|
||||
g_assert_false(playlist_new(PL_LIBRARY, "tests/Music/Hyrule Symphony/01 - Title Theme.ogg"));
|
||||
g_assert_null(playlist_new(PL_LIBRARY, "tests/Music/Hyrule Symphony/01 - Title Theme.ogg"));
|
||||
g_assert_null(playlist_lookup(PL_LIBRARY, "tests/Music"));
|
||||
|
||||
g_assert_null(playlist_get_queue(PL_LIBRARY, "tests/Music"));
|
||||
g_assert_false(playlist_select(PL_LIBRARY, "tests/Music"));
|
||||
g_assert_true(playlist_new(PL_LIBRARY, "tests/Music"));
|
||||
g_assert_false(playlist_new(PL_LIBRARY, "tests/Music"));
|
||||
g_assert_nonnull(playlist_get_queue(PL_LIBRARY, "tests/Music"));
|
||||
g_assert_false(playlist_select(NULL));
|
||||
|
||||
g_assert_cmpuint(playlist_get_id(PL_LIBRARY, "tests/Music"), ==, 0);
|
||||
g_assert_cmpstr_free(playlist_get_name(PL_LIBRARY, 0), ==, "tests/Music");
|
||||
g_assert_false(playlist_select(PL_LIBRARY, "tests/Music"));
|
||||
playlist = playlist_new(PL_LIBRARY, "tests/Music");
|
||||
g_assert_nonnull(playlist);
|
||||
g_assert_null(playlist_new(PL_LIBRARY, "tests/Music"));
|
||||
g_assert_nonnull(playlist_lookup(PL_LIBRARY, "tests/Music"));
|
||||
|
||||
library = library_get(0);
|
||||
playlist = library->li_playlist;
|
||||
g_assert_nonnull(library);
|
||||
g_assert_nonnull(library->li_playlist);
|
||||
g_assert_nonnull(playlist);
|
||||
g_assert_cmpuint(playlist->pl_id, ==, library_index(library));
|
||||
|
||||
g_assert_cmpuint(playlist_size(PL_LIBRARY, "tests/Music"), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 0);
|
||||
while (idle_run_task()) {};
|
||||
g_assert_cmpuint(playlist_size(PL_LIBRARY, "tests/Music"), ==, 48);
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, "Unplayed"), ==, 48);
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, "Collection"), ==, 48);
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 48);
|
||||
g_assert_cmpuint(playlist_size(playlist_lookup(PL_SYSTEM, "Unplayed")),
|
||||
==, 48);
|
||||
g_assert_cmpuint(playlist_size(playlist_lookup(PL_SYSTEM, "Collection")),
|
||||
==, 48);
|
||||
|
||||
g_assert_true(playlist_select(PL_LIBRARY, "tests/Music"));
|
||||
playlist = playlist_lookup(PL_LIBRARY, "tests/Music");
|
||||
g_assert(playlist_current() != playlist);
|
||||
g_assert_true(playlist_select(playlist));
|
||||
g_assert_cmpuint(settings_get("core.playlist.cur.type"), ==, PL_LIBRARY);
|
||||
g_assert_cmpuint(settings_get("core.playlist.cur.id"), ==, 0);
|
||||
g_assert(playlist_current() == playlist);
|
||||
g_assert_false(playlist_select(playlist));
|
||||
|
||||
g_assert_false(playlist_add(PL_LIBRARY, "tests/Music", track_get(0)));
|
||||
g_assert_false(playlist_add(PL_LIBRARY, "tests/Music", track_get(1)));
|
||||
g_assert_false(playlist_remove(PL_LIBRARY, "tests/Music", track_get(0)));
|
||||
g_assert_false(playlist_remove(PL_LIBRARY, "tests/Music", track_get(1)));
|
||||
g_assert_false(playlist_add(playlist, track_get(0)));
|
||||
g_assert_false(playlist_add(playlist, track_get(1)));
|
||||
g_assert_false(playlist_remove(playlist, track_get(0)));
|
||||
g_assert_false(playlist_remove(playlist, track_get(1)));
|
||||
|
||||
pl_library_deinit();
|
||||
g_assert_null(playlist_get_queue(PL_LIBRARY, "tests/Music"));
|
||||
g_assert_null(playlist_lookup(PL_LIBRARY, "tests/Music"));
|
||||
g_assert_null(library->li_playlist);
|
||||
pl_library_init(NULL);
|
||||
pl_library_init();
|
||||
while (idle_run_task()) {};
|
||||
|
||||
g_assert_nonnull(library->li_playlist);
|
||||
g_assert_nonnull(playlist_get_queue(PL_LIBRARY, "tests/Music"));
|
||||
g_assert_cmpuint(playlist_size(PL_LIBRARY, "tests/Music"), ==, 48);
|
||||
g_assert(playlist_get_queue(PL_LIBRARY, "tests/Music"));
|
||||
|
||||
g_assert_false(playlist_add(PL_LIBRARY, "tests/Music", track_get(0)));
|
||||
g_assert_false(playlist_add(PL_LIBRARY, "tests/Music", track_get(1)));
|
||||
g_assert_false(playlist_remove(PL_LIBRARY, "tests/Music", track_get(0)));
|
||||
g_assert_false(playlist_remove(PL_LIBRARY, "tests/Music", track_get(1)));
|
||||
g_assert_cmpuint(playlist_size(PL_LIBRARY, "tests/Music"), ==, 48);
|
||||
|
||||
g_assert_false(playlist_get_random(PL_LIBRARY, "tests/Music"));
|
||||
playlist_set_random(PL_LIBRARY, "tests/Music", true);
|
||||
g_assert_true(playlist_get_random(PL_LIBRARY, "tests/Music"));
|
||||
playlist_set_random(PL_LIBRARY, "tests/Music", false);
|
||||
g_assert_false(playlist_get_random(PL_LIBRARY, "tests/Music"));
|
||||
|
||||
playlist = library->li_playlist;
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_queue.q_sort), ==, 3);
|
||||
playlist_sort(PL_LIBRARY, "tests/Music", COMPARE_ARTIST, true);
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_queue.q_sort), ==, 1);
|
||||
playlist_sort(PL_LIBRARY, "tests/Music", COMPARE_YEAR, false);
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_queue.q_sort), ==, 2);
|
||||
playlist_sort(PL_LIBRARY, "tests/Music", COMPARE_TRACK, false);
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_queue.q_sort), ==, 3);
|
||||
g_assert_nonnull(library->li_playlist);
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 48);
|
||||
|
||||
g_assert_false(playlist_add(playlist, track_get(0)));
|
||||
g_assert_false(playlist_add(playlist, track_get(1)));
|
||||
g_assert_false(playlist_remove(playlist, track_get(0)));
|
||||
g_assert_false(playlist_remove(playlist, track_get(1)));
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 48);
|
||||
|
||||
playlist_selected(track_get(1));
|
||||
playlist_played(track_get(1));
|
||||
|
||||
g_assert_false(playlist->pl_random);
|
||||
playlist_set_random(playlist, true);
|
||||
g_assert_true(playlist->pl_random);
|
||||
playlist_set_random(playlist, false);
|
||||
g_assert_false(playlist->pl_random);
|
||||
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_sort), ==, 4);
|
||||
g_assert_true(playlist_rearrange(playlist, 15, 20));
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_sort), ==, 0);
|
||||
g_assert_true(playlist_sort(playlist, COMPARE_ARTIST));
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_sort), ==, 1);
|
||||
g_assert_true(playlist_sort(playlist, COMPARE_YEAR));
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_sort), ==, 2);
|
||||
g_assert_true(playlist_sort(playlist, COMPARE_TRACK));
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_sort), ==, 3);
|
||||
|
||||
g_rename("tests/Music/Hyrule Symphony/", "tests/Hyrule Symphony/");
|
||||
playlist_update(PL_LIBRARY, "tests/Music");
|
||||
pl_library_update(playlist);
|
||||
while (idle_run_task()) {}
|
||||
g_assert_cmpuint(db_actual_size(track_db_get()), ==, 48);
|
||||
g_assert_cmpuint(track_db_get()->db_size, ==, 35);
|
||||
g_assert_cmpuint(playlist_size(PL_LIBRARY, "tests/Music"), ==, 35);
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 35);
|
||||
|
||||
g_rename("tests/Hyrule Symphony", "tests/Music/Hyrule Symphony/");
|
||||
playlist_update(PL_LIBRARY, "tests/Music");
|
||||
pl_library_update(playlist);
|
||||
while (idle_run_task()) {}
|
||||
g_assert_cmpuint(db_actual_size(track_db_get()), ==, 61);
|
||||
g_assert_cmpuint(track_db_get()->db_size, ==, 48);
|
||||
g_assert_cmpuint(playlist_size(PL_LIBRARY, "tests/Music"), ==, 48);
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 48);
|
||||
|
||||
g_assert_true( playlist_delete(PL_LIBRARY, "tests/Music"));
|
||||
g_assert_false(playlist_delete(PL_LIBRARY, "tests/Music"));
|
||||
g_assert_true(playlist_delete(playlist));
|
||||
g_assert_null(library_get(0));
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, "Unplayed"), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, "Collection"), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(playlist_lookup(PL_SYSTEM, "Unplayed")),
|
||||
==, 0);
|
||||
g_assert_cmpuint(playlist_size(playlist_lookup(PL_SYSTEM, "Collection")),
|
||||
==, 0);
|
||||
g_assert_cmpuint(track_db_get()->db_size, ==, 0);
|
||||
}
|
||||
|
||||
|
@ -102,7 +112,7 @@ int main(int argc, char **argv)
|
|||
{
|
||||
int ret;
|
||||
|
||||
idle_init_sync();
|
||||
idle_init(IDLE_SYNC);
|
||||
settings_init();
|
||||
tags_init();
|
||||
playlist_init(NULL);
|
||||
|
|
|
@ -8,416 +8,387 @@
|
|||
#include <tests/test.h>
|
||||
|
||||
|
||||
#define __test_playlist_id(name, id) \
|
||||
g_assert_cmpuint(playlist_get_id(PL_SYSTEM, name), ==, id); \
|
||||
g_assert_cmpstr_free(playlist_get_name(PL_SYSTEM, id), ==, name)
|
||||
|
||||
#define __test_playlist_has(name, track, expected) \
|
||||
if (expected) \
|
||||
g_assert_true(playlist_has(PL_SYSTEM, name, track)); \
|
||||
else \
|
||||
g_assert_false(playlist_has(PL_SYSTEM, name, track))
|
||||
|
||||
#define __test_playlist_state(name, ex_size, ex_track0, ex_track1) \
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, name), ==, ex_size); \
|
||||
__test_playlist_has(name, track_get(0), ex_track0); \
|
||||
__test_playlist_has(name, track_get(1), ex_track1)
|
||||
|
||||
#define __test_playlist_random(name) \
|
||||
g_assert_false(playlist_get_random(PL_SYSTEM, name)); \
|
||||
playlist_set_random(PL_SYSTEM, name, true); \
|
||||
g_assert_true(playlist_get_random(PL_SYSTEM, name)); \
|
||||
playlist_set_random(PL_SYSTEM, name, false); \
|
||||
g_assert_false(playlist_get_random(PL_SYSTEM, name))
|
||||
|
||||
#define __test_playlist_add(name) \
|
||||
__test_playlist_state(name, 0, false, false); \
|
||||
g_assert_true( playlist_add(PL_SYSTEM, name, track_get(0))); \
|
||||
g_assert_false(playlist_add(PL_SYSTEM, name, track_get(0))); \
|
||||
g_assert_true( playlist_add(PL_SYSTEM, name, track_get(1))); \
|
||||
g_assert_false(playlist_add(PL_SYSTEM, name, track_get(1))); \
|
||||
__test_playlist_state(name, 2, true, true)
|
||||
|
||||
#define __test_playlist_remove(name) \
|
||||
g_assert_true( playlist_remove(PL_SYSTEM, name, track_get(0))); \
|
||||
g_assert_false(playlist_remove(PL_SYSTEM, name, track_get(0))); \
|
||||
g_assert_true( playlist_remove(PL_SYSTEM, name, track_get(1))); \
|
||||
g_assert_false(playlist_remove(PL_SYSTEM, name, track_get(1))); \
|
||||
__test_playlist_state(name, 0, false, false)
|
||||
|
||||
#define __test_playlist_clear(name, ex_size, ex_track0, ex_track1) \
|
||||
g_assert_false(playlist_delete(PL_SYSTEM, name)); \
|
||||
__test_playlist_state(name, ex_size, ex_track0, ex_track1)
|
||||
|
||||
#define __test_playlist_update(name, ex_size, ex_track0, ex_track1) \
|
||||
playlist_update(PL_SYSTEM, name); \
|
||||
while (idle_run_task()) {}; \
|
||||
__test_playlist_state(name, ex_size, ex_track0, ex_track1)
|
||||
|
||||
#define __test_playlist_hide_track(name, ex_size, ex_track0, ex_track1) \
|
||||
g_assert_true(playlist_add(PL_SYSTEM, "Hidden", track_get(0))); \
|
||||
__test_playlist_state(name, ex_size, ex_track0, ex_track1); \
|
||||
g_assert_false(playlist_add(PL_SYSTEM, name, track_get(0)))
|
||||
|
||||
#define __test_playlist_unhide_track(name, ex_size, ex_track0, ex_track1) \
|
||||
g_assert_true(playlist_remove(PL_SYSTEM, "Hidden", track_get(0))); \
|
||||
__test_playlist_state(name, ex_size, ex_track0, ex_track1)
|
||||
|
||||
#define __test_playlist_clear_hidden(name, ex_size, ex_track0, ex_track1) \
|
||||
__test_playlist_clear("Hidden", 0, false, false); \
|
||||
__test_playlist_state(name, ex_size, ex_track0, ex_track1);
|
||||
|
||||
#define __test_playlist_select(name, id) \
|
||||
g_assert_true(playlist_select(PL_SYSTEM, name)); \
|
||||
g_assert_cmpuint(settings_get("core.playlist.cur.id"), ==, id)
|
||||
|
||||
#define __test_playlist_noselect(name) \
|
||||
do { \
|
||||
unsigned int id = settings_get("core.playlist.cur.id"); \
|
||||
g_assert_false(playlist_select(PL_SYSTEM, name)); \
|
||||
g_assert_cmpuint(settings_get("core.playlist.cur.id"), ==, id); \
|
||||
} while (0)
|
||||
|
||||
#define __test_playlist_reinit(name, ex_size, ex_track0, ex_track1) \
|
||||
do { \
|
||||
struct queue *queue; \
|
||||
pl_system_deinit(); \
|
||||
pl_system_init(NULL); \
|
||||
queue = playlist_get_queue(PL_SYSTEM, name); \
|
||||
g_assert_nonnull(queue); \
|
||||
__test_playlist_state(name, 0, false, false); \
|
||||
while (idle_run_task()) {}; \
|
||||
g_assert_false(queue_has_flag(queue, Q_ADD_FRONT)); \
|
||||
__test_playlist_state(name, ex_size, ex_track0, ex_track1); \
|
||||
} while (0)
|
||||
static inline struct playlist *__test_pl_favorites(void)
|
||||
{ return playlist_lookup(PL_SYSTEM, "Favorites"); }
|
||||
static inline struct playlist *__test_pl_hidden(void)
|
||||
{ return playlist_lookup(PL_SYSTEM, "Hidden"); }
|
||||
static inline struct playlist *__test_pl_queued(void)
|
||||
{ return playlist_lookup(PL_SYSTEM, "Queued Tracks"); }
|
||||
static inline struct playlist *__test_pl_collection(void)
|
||||
{ return playlist_lookup(PL_SYSTEM, "Collection"); }
|
||||
static inline struct playlist *__test_pl_history(void)
|
||||
{ return playlist_lookup(PL_SYSTEM, "History"); }
|
||||
static inline struct playlist *__test_pl_unplayed(void)
|
||||
{ return playlist_lookup(PL_SYSTEM, "Unplayed"); }
|
||||
static inline struct playlist *__test_pl_most_played(void)
|
||||
{ return playlist_lookup(PL_SYSTEM, "Most Played"); }
|
||||
static inline struct playlist *__test_pl_least_played(void)
|
||||
{ return playlist_lookup(PL_SYSTEM, "Least Played"); }
|
||||
|
||||
|
||||
static void test_invalid()
|
||||
static void test_init()
|
||||
{
|
||||
g_assert_null(playlist_get_queue(PL_SYSTEM, NULL));
|
||||
g_assert_null(playlist_get_queue(PL_SYSTEM, "Invalid"));
|
||||
struct library *library = library_find("tests/Music");
|
||||
struct playlist *playlist;
|
||||
unsigned int i;
|
||||
|
||||
g_assert_cmpuint(playlist_get_id(PL_SYSTEM, NULL), ==,
|
||||
SYS_PL_NUM_PLAYLISTS);
|
||||
g_assert_cmpuint(playlist_get_id(PL_SYSTEM, "Invalid"), ==,
|
||||
SYS_PL_NUM_PLAYLISTS);
|
||||
g_assert_null(playlist_get_name(PL_SYSTEM, SYS_PL_NUM_PLAYLISTS));
|
||||
g_assert(playlist_current() == __test_pl_collection());
|
||||
|
||||
g_assert_false(playlist_new(PL_SYSTEM, "New Playlist"));
|
||||
g_assert_false(playlist_delete(PL_SYSTEM, "Favorites"));
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) {
|
||||
playlist = playlist_get(PL_SYSTEM, i);
|
||||
g_assert_nonnull(playlist);
|
||||
g_assert(playlist_lookup(PL_SYSTEM, playlist->pl_name) == playlist);
|
||||
g_assert_cmpuint(playlist->pl_id, ==, i);
|
||||
g_assert_false(playlist_select(playlist));
|
||||
|
||||
__test_playlist_noselect(NULL);
|
||||
__test_playlist_noselect("Invalid");
|
||||
if (i == SYS_PL_QUEUED || i == SYS_PL_HISTORY) {
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_sort), ==, 0);
|
||||
} else
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_sort), ==, 4);
|
||||
}
|
||||
|
||||
playlist_update(PL_SYSTEM, NULL);
|
||||
g_assert_false(playlist_add(PL_SYSTEM, NULL, track_get(0)));
|
||||
g_assert_false(playlist_remove(PL_SYSTEM, NULL, track_get(0)));
|
||||
g_assert_false(playlist_delete(PL_SYSTEM, NULL));
|
||||
g_assert_false(playlist_has(PL_SYSTEM, NULL, track_get(0)));
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, NULL), ==, 0);
|
||||
/* Add tracks to the collection. */
|
||||
track_add(library, "tests/Music/Hyrule Symphony/01 - Title Theme.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/02 - Kokiri Forest.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/03 - Hyrule Field.ogg");
|
||||
pl_system_new_track(track_get(0));
|
||||
pl_system_new_track(track_get(1));
|
||||
pl_system_new_track(track_get(2));
|
||||
|
||||
g_assert_false(playlist_get_random(PL_SYSTEM, NULL));
|
||||
playlist_set_random(PL_SYSTEM, NULL, true);
|
||||
g_assert_false(playlist_get_random(PL_SYSTEM, NULL));
|
||||
g_assert_null(playlist_new(PL_SYSTEM, "New Playlist"));
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) {
|
||||
playlist = playlist_get(PL_SYSTEM, i);
|
||||
|
||||
if (i == SYS_PL_COLLECTION || i == SYS_PL_UNPLAYED)
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 3);
|
||||
else
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_favorites()
|
||||
static void test_random()
|
||||
{
|
||||
struct queue *queue = playlist_get_queue(PL_SYSTEM, "Favorites");
|
||||
struct playlist *playlist;
|
||||
unsigned int i;
|
||||
|
||||
g_assert_nonnull(queue);
|
||||
g_assert_false(queue_has_flag(queue, Q_ADD_FRONT));
|
||||
g_assert_true(queue_has_flag(queue, Q_REPEAT));
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) {
|
||||
playlist = playlist_get(PL_SYSTEM, i);
|
||||
|
||||
__test_playlist_id("Favorites", SYS_PL_FAVORITES);
|
||||
__test_playlist_noselect("Favorites");
|
||||
__test_playlist_random("Favorites");
|
||||
__test_playlist_add("Favorites");
|
||||
__test_playlist_select("Favorites", SYS_PL_FAVORITES);
|
||||
__test_playlist_reinit("Favorites", 2, true, true);
|
||||
__test_playlist_update("Favorites", 2, true, true);
|
||||
__test_playlist_clear("Favorites", 0, false, false);
|
||||
__test_playlist_reinit("Favorites", 0, false, false);
|
||||
__test_playlist_add("Favorites");
|
||||
__test_playlist_remove("Favorites");
|
||||
g_assert_false(playlist->pl_random);
|
||||
playlist_set_random(playlist, true);
|
||||
if (i == SYS_PL_HISTORY)
|
||||
g_assert_false(playlist->pl_random);
|
||||
else
|
||||
g_assert_true(playlist->pl_random);
|
||||
|
||||
playlist_set_random(playlist, false);
|
||||
g_assert_false(playlist->pl_random);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_hidden()
|
||||
static void test_sort()
|
||||
{
|
||||
struct queue *queue = playlist_get_queue(PL_SYSTEM, "Hidden");
|
||||
struct playlist *playlist;
|
||||
unsigned int i;
|
||||
|
||||
g_assert_nonnull(queue);
|
||||
g_assert_null(playlist_get_queue(PL_SYSTEM, "Banned"));
|
||||
g_assert_false(queue_has_flag(queue, Q_ADD_FRONT));
|
||||
g_assert_true(queue_has_flag(queue, Q_REPEAT));
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) {
|
||||
playlist = playlist_get(PL_SYSTEM, i);
|
||||
playlist_clear_sort(playlist);
|
||||
|
||||
__test_playlist_id("Hidden", SYS_PL_HIDDEN);
|
||||
__test_playlist_noselect("Hidden");
|
||||
__test_playlist_random("Hidden");
|
||||
__test_playlist_add("Hidden");
|
||||
__test_playlist_select("Hidden", SYS_PL_HIDDEN);
|
||||
__test_playlist_reinit("Hidden", 2, true, true);
|
||||
__test_playlist_update("Hidden", 2, true, true);
|
||||
__test_playlist_clear("Hidden", 0, false, false);
|
||||
__test_playlist_reinit("Hidden", 0, false, false);
|
||||
__test_playlist_add("Hidden");
|
||||
__test_playlist_remove("Hidden");
|
||||
if (i == SYS_PL_HISTORY) {
|
||||
g_assert_false(playlist_sort(playlist, COMPARE_TRACK));
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_sort), ==, 0);
|
||||
g_assert_false(playlist_sort(playlist, COMPARE_YEAR));
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_sort), ==, 0);
|
||||
} else {
|
||||
g_assert_true(playlist_sort(playlist, COMPARE_TRACK));
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_sort), ==, 1);
|
||||
g_assert_true(playlist_sort(playlist, COMPARE_YEAR));
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_sort), ==, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_queued()
|
||||
static void test_played()
|
||||
{
|
||||
struct queue *queue = playlist_get_queue(PL_SYSTEM, "Queued Tracks");
|
||||
g_assert_cmpuint(playlist_size(__test_pl_unplayed()), ==, 3);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_most_played()), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_least_played()), ==, 0);
|
||||
|
||||
g_assert_nonnull(queue);
|
||||
g_assert_false(queue_has_flag(queue, Q_ADD_FRONT));
|
||||
g_assert_false(queue_has_flag(queue, Q_NO_SORT));
|
||||
g_assert_false(queue_has_flag(queue, Q_REPEAT));
|
||||
g_assert_cmpuint(g_slist_length(queue->q_sort), ==, 0);
|
||||
track_played(track_get(0));
|
||||
playlist_played(track_get(0));
|
||||
while (idle_run_task()) {}
|
||||
|
||||
__test_playlist_id("Queued Tracks", SYS_PL_QUEUED);
|
||||
__test_playlist_noselect("Queued Tracks");
|
||||
__test_playlist_random("Queued Tracks");
|
||||
__test_playlist_add("Queued Tracks");
|
||||
__test_playlist_select("Queued Tracks", SYS_PL_QUEUED);
|
||||
__test_playlist_reinit("Queued Tracks", 2, true, true);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_unplayed()), ==, 2);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_most_played()), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_least_played()), ==, 1);
|
||||
|
||||
g_assert(playlist_next() == track_get(0));
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, "Queued Tracks"), ==, 1);
|
||||
playlist_select(PL_SYSTEM, "Collection");
|
||||
g_assert(playlist_next() == track_get(0));
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, "Queued Tracks"), ==, 1);
|
||||
playlist_select(PL_SYSTEM, "Queued Tracks");
|
||||
g_assert(playlist_next() == track_get(1));
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, "Queued Tracks"), ==, 0);
|
||||
g_assert(playlist_next() == track_get(1));
|
||||
g_assert_true(playlist_has(__test_pl_least_played(), track_get(0)));
|
||||
g_assert_true(playlist_has(__test_pl_unplayed(), track_get(1)));
|
||||
g_assert_true(playlist_has(__test_pl_unplayed(), track_get(2)));
|
||||
|
||||
__test_playlist_add("Queued Tracks");
|
||||
__test_playlist_remove("Queued Tracks");
|
||||
__test_playlist_update("Queued Tracks", 0, false, false);
|
||||
__test_playlist_add("Queued Tracks");
|
||||
__test_playlist_clear("Queued Tracks", 0, false, false);
|
||||
__test_playlist_reinit("Queued Tracks", 0, false, false);
|
||||
}
|
||||
|
||||
static void test_collection()
|
||||
{
|
||||
struct queue *queue = playlist_get_queue(PL_SYSTEM, "Collection");
|
||||
|
||||
pl_system_deinit();
|
||||
pl_system_init(NULL);
|
||||
|
||||
g_assert_nonnull(queue);
|
||||
g_assert_true(queue_has_flag(queue, Q_ADD_FRONT));
|
||||
g_assert_true(queue_has_flag(queue, Q_REPEAT));
|
||||
|
||||
__test_playlist_id("Collection", SYS_PL_COLLECTION);
|
||||
__test_playlist_select("Collection", SYS_PL_COLLECTION);
|
||||
__test_playlist_random("Collection");
|
||||
__test_playlist_add("Collection");
|
||||
__test_playlist_hide_track("Collection", 1, false, true);
|
||||
__test_playlist_reinit("Collection", 1, false, true);
|
||||
__test_playlist_update("Collection", 1, false, true);
|
||||
__test_playlist_unhide_track("Collection", 2, true, true);
|
||||
__test_playlist_hide_track("Collection", 1, false, true);
|
||||
__test_playlist_clear_hidden("Collection", 2, true, true);
|
||||
__test_playlist_clear("Collection", 2, true, true);
|
||||
}
|
||||
|
||||
static void test_history()
|
||||
{
|
||||
struct queue *queue = playlist_get_queue(PL_SYSTEM, "History");
|
||||
|
||||
g_assert_nonnull(queue);
|
||||
g_assert_true(queue_has_flag(queue, Q_ADD_FRONT));
|
||||
g_assert_true(queue_has_flag(queue, Q_NO_SORT));
|
||||
g_assert_true(queue_has_flag(queue, Q_REPEAT));
|
||||
__test_playlist_id("History", SYS_PL_HISTORY);
|
||||
__test_playlist_noselect("History");
|
||||
|
||||
g_assert_cmpuint(g_slist_length(queue->q_sort), ==, 0);
|
||||
playlist_sort(PL_SYSTEM, "History", COMPARE_TRACK, true);
|
||||
g_assert_cmpuint(g_slist_length(queue->q_sort), ==, 0);
|
||||
|
||||
g_assert_false(playlist_get_random(PL_SYSTEM, "History"));
|
||||
playlist_set_random(PL_SYSTEM, "History", true);
|
||||
g_assert_false(playlist_get_random(PL_SYSTEM, "History"));
|
||||
|
||||
__test_playlist_state("History", 0, false, false);
|
||||
g_assert_true(playlist_add(PL_SYSTEM, "History", track_get(0)));
|
||||
g_assert_true(playlist_add(PL_SYSTEM, "History", track_get(0)));
|
||||
g_assert_true(playlist_add(PL_SYSTEM, "History", track_get(1)));
|
||||
g_assert_true(playlist_add(PL_SYSTEM, "History", track_get(1)));
|
||||
__test_playlist_state("History", 4, true, true);
|
||||
__test_playlist_noselect("History");
|
||||
|
||||
g_assert(playlist_prev() == track_get(1));
|
||||
g_assert(playlist_prev() == track_get(0));
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, "History"), ==, 4);
|
||||
g_assert_true(playlist_add(PL_SYSTEM, "History", track_get(1)));
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, "History"), ==, 5);
|
||||
g_assert(playlist_prev() == track_get(1));
|
||||
|
||||
__test_playlist_clear("History", 5, true, true);
|
||||
__test_playlist_remove("History");
|
||||
__test_playlist_update("History", 0, false, false);
|
||||
}
|
||||
|
||||
static void test_unplayed()
|
||||
{
|
||||
struct queue *queue = playlist_get_queue(PL_SYSTEM, "Unplayed");
|
||||
|
||||
pl_system_deinit();
|
||||
pl_system_init(NULL);
|
||||
|
||||
g_assert_nonnull(queue);
|
||||
g_assert_true(queue_has_flag(queue, Q_ADD_FRONT));
|
||||
g_assert_true(queue_has_flag(queue, Q_REPEAT));
|
||||
|
||||
__test_playlist_id("Unplayed", SYS_PL_UNPLAYED);
|
||||
__test_playlist_noselect("Unplayed");
|
||||
__test_playlist_random("Unplayed");
|
||||
__test_playlist_reinit("Unplayed", 2, true, true);
|
||||
__test_playlist_remove("Unplayed");
|
||||
|
||||
track_get(1)->tr_count = 1;
|
||||
g_assert_true( playlist_add(PL_SYSTEM, "Unplayed", track_get(0)));
|
||||
g_assert_false(playlist_add(PL_SYSTEM, "Unplayed", track_get(1)));
|
||||
g_assert_cmpuint(playlist_size(PL_SYSTEM, "Unplayed"), ==, 1);
|
||||
__test_playlist_select("Unplayed", SYS_PL_UNPLAYED);
|
||||
|
||||
track_get(0)->tr_count = 1;
|
||||
track_get(1)->tr_count = 0;
|
||||
__test_playlist_update("Unplayed", 1, false, true);
|
||||
|
||||
track_get(0)->tr_count = 0;
|
||||
__test_playlist_update("Unplayed", 2, true, true);
|
||||
|
||||
__test_playlist_hide_track("Unplayed", 1, false, true);
|
||||
__test_playlist_reinit("Unplayed", 1, false, true);
|
||||
__test_playlist_update("Unplayed", 1, false, true);
|
||||
__test_playlist_unhide_track("Unplayed", 2, true, true);
|
||||
__test_playlist_hide_track("Unplayed", 1, false, true);
|
||||
__test_playlist_clear_hidden("Unplayed", 2, true, true);
|
||||
__test_playlist_clear("Unplayed", 2, true, true);
|
||||
}
|
||||
|
||||
static void test_most_played()
|
||||
{
|
||||
struct queue *most = playlist_get_queue(PL_SYSTEM, "Most Played");
|
||||
|
||||
/* Set average = (4 / 2) = 2 */
|
||||
track_played(track_get(0));
|
||||
track_played(track_get(0));
|
||||
track_played(track_get(1));
|
||||
track_played(track_get(1));
|
||||
track_played(track_get(1));
|
||||
playlist_played(track_get(0));
|
||||
playlist_played(track_get(1));
|
||||
while (idle_run_task()) {}
|
||||
|
||||
pl_system_deinit();
|
||||
pl_system_init(NULL);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_unplayed()), ==, 1);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_most_played()), ==, 1);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_least_played()), ==, 1);
|
||||
|
||||
g_assert_nonnull(most);
|
||||
g_assert_true(queue_has_flag(most, Q_ADD_FRONT));
|
||||
g_assert_true(queue_has_flag(most, Q_REPEAT));
|
||||
|
||||
__test_playlist_id("Most Played", SYS_PL_MOST_PLAYED);
|
||||
__test_playlist_noselect("Most Played");
|
||||
__test_playlist_random("Most Played");
|
||||
__test_playlist_reinit("Most Played", 1, false, true);
|
||||
|
||||
g_assert_false(playlist_remove(PL_SYSTEM, "Most Played", track_get(0)));
|
||||
g_assert_true( playlist_remove(PL_SYSTEM, "Most Played", track_get(1)));
|
||||
g_assert_false(playlist_remove(PL_SYSTEM, "Most Played", track_get(1)));
|
||||
__test_playlist_state("Most Played", 0, false, false);
|
||||
|
||||
g_assert_false(playlist_add(PL_SYSTEM, "Most Played", track_get(0)));
|
||||
g_assert_true( playlist_add(PL_SYSTEM, "Most Played", track_get(1)));
|
||||
g_assert_false(playlist_add(PL_SYSTEM, "Most Played", track_get(1)));
|
||||
__test_playlist_state("Most Played", 1, false, true);
|
||||
__test_playlist_select("Most Played", SYS_PL_MOST_PLAYED);
|
||||
|
||||
track_get(0)->tr_count = 3;
|
||||
track_get(1)->tr_count = 1;
|
||||
__test_playlist_update("Most Played", 1, true, false);
|
||||
|
||||
__test_playlist_hide_track("Most Played", 0, false, false);
|
||||
__test_playlist_reinit("Most Played", 0, false, false);
|
||||
__test_playlist_update("Most Played", 0, false, false);
|
||||
__test_playlist_unhide_track("Most Played", 1, true, false);
|
||||
__test_playlist_hide_track("Most Played", 0, false, false);
|
||||
__test_playlist_clear_hidden("Most Played", 1, true, false);
|
||||
__test_playlist_clear("Most Played", 1, true, false);
|
||||
g_assert_true(playlist_has(__test_pl_most_played(), track_get(0)));
|
||||
g_assert_true(playlist_has(__test_pl_least_played(), track_get(1)));
|
||||
g_assert_true(playlist_has(__test_pl_unplayed(), track_get(2)));
|
||||
}
|
||||
|
||||
static void test_least_played()
|
||||
static void test_add()
|
||||
{
|
||||
struct queue *least = playlist_get_queue(PL_SYSTEM, "Least Played");
|
||||
struct playlist *playlist;
|
||||
unsigned int i;
|
||||
|
||||
/* Reset playcounts so track 1 is "least played" */
|
||||
track_get(0)->tr_count = 3;
|
||||
track_get(1)->tr_count = 1;
|
||||
g_assert_true( playlist_add(__test_pl_favorites(), track_get(0)));
|
||||
g_assert_false(playlist_add(__test_pl_favorites(), track_get(0)));
|
||||
g_assert_true( playlist_add(__test_pl_queued(), track_get(0)));
|
||||
g_assert_false(playlist_add(__test_pl_queued(), track_get(0)));
|
||||
g_assert_true( playlist_add(__test_pl_history(), track_get(0)));
|
||||
g_assert_true( playlist_add(__test_pl_history(), track_get(0)));
|
||||
|
||||
pl_system_deinit();
|
||||
pl_system_init(NULL);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_favorites()), ==, 1);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_queued()), ==, 1);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_history()), ==, 2);
|
||||
|
||||
g_assert_nonnull(least);
|
||||
g_assert_true(queue_has_flag(least, Q_ADD_FRONT));
|
||||
g_assert_true(queue_has_flag(least, Q_REPEAT));
|
||||
g_assert_true(playlist_has(__test_pl_favorites(), track_get(0)));
|
||||
g_assert_true(playlist_has(__test_pl_queued(), track_get(0)));
|
||||
g_assert_true(playlist_has(__test_pl_history(), track_get(0)));
|
||||
|
||||
__test_playlist_id("Least Played", SYS_PL_LEAST_PLAYED);
|
||||
__test_playlist_noselect("Least Played");
|
||||
__test_playlist_random("Least Played");
|
||||
__test_playlist_reinit("Least Played", 1, false, true);
|
||||
g_assert_true( playlist_add(__test_pl_hidden(), track_get(0)));
|
||||
g_assert_true( playlist_add(__test_pl_hidden(), track_get(1)));
|
||||
g_assert_true( playlist_add(__test_pl_hidden(), track_get(2)));
|
||||
g_assert_false(playlist_add(__test_pl_collection(), track_get(0)));
|
||||
g_assert_false(playlist_add(__test_pl_collection(), track_get(1)));
|
||||
g_assert_false(playlist_add(__test_pl_collection(), track_get(2)));
|
||||
g_assert_false(playlist_add(__test_pl_most_played(), track_get(0)));
|
||||
g_assert_false(playlist_add(__test_pl_least_played(), track_get(1)));
|
||||
g_assert_false(playlist_add(__test_pl_unplayed(), track_get(2)));
|
||||
|
||||
g_assert_false(playlist_remove(PL_SYSTEM, "Least Played", track_get(0)));
|
||||
g_assert_true( playlist_remove(PL_SYSTEM, "Least Played", track_get(1)));
|
||||
g_assert_false(playlist_remove(PL_SYSTEM, "Least Played", track_get(1)));
|
||||
__test_playlist_state("Least Played", 0, false, false);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_hidden()), ==, 3);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_collection()), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_most_played()), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_least_played()), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_unplayed()), ==, 0);
|
||||
|
||||
g_assert_false(playlist_add(PL_SYSTEM, "Least Played", track_get(0)));
|
||||
g_assert_true( playlist_add(PL_SYSTEM, "Least Played", track_get(1)));
|
||||
g_assert_false(playlist_add(PL_SYSTEM, "Least Played", track_get(1)));
|
||||
__test_playlist_state("Least Played", 1, false, true);
|
||||
__test_playlist_select("Least Played", SYS_PL_LEAST_PLAYED);
|
||||
g_assert_true( playlist_has(__test_pl_hidden(), track_get(0)));
|
||||
g_assert_true( playlist_has(__test_pl_hidden(), track_get(1)));
|
||||
g_assert_true( playlist_has(__test_pl_hidden(), track_get(2)));
|
||||
g_assert_false(playlist_has(__test_pl_collection(), track_get(0)));
|
||||
g_assert_false(playlist_has(__test_pl_collection(), track_get(1)));
|
||||
g_assert_false(playlist_has(__test_pl_collection(), track_get(2)));
|
||||
g_assert_false(playlist_has(__test_pl_most_played(), track_get(0)));
|
||||
g_assert_false(playlist_has(__test_pl_least_played(), track_get(1)));
|
||||
g_assert_false(playlist_has(__test_pl_unplayed(), track_get(2)));
|
||||
|
||||
track_get(0)->tr_count = 1;
|
||||
track_get(1)->tr_count = 3;
|
||||
__test_playlist_update("Least Played", 1, true, false);
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) {
|
||||
playlist = playlist_get(PL_SYSTEM, i);
|
||||
|
||||
__test_playlist_hide_track("Least Played", 0, false, false);
|
||||
__test_playlist_reinit("Least Played", 0, false, false);
|
||||
__test_playlist_update("Least Played", 0, false, false);
|
||||
__test_playlist_unhide_track("Least Played", 1, true, false);
|
||||
__test_playlist_hide_track("Least Played", 0, false, false);
|
||||
__test_playlist_clear_hidden("Least Played", 1, true, false);
|
||||
__test_playlist_clear("Least Played", 1, true, false);
|
||||
switch (i) {
|
||||
case SYS_PL_FAVORITES:
|
||||
case SYS_PL_HIDDEN:
|
||||
case SYS_PL_QUEUED:
|
||||
g_assert_true( playlist_select(playlist));
|
||||
g_assert_false(playlist_select(playlist));
|
||||
g_assert(playlist_current() == playlist);
|
||||
break;
|
||||
default:
|
||||
g_assert_false(playlist_select(playlist));
|
||||
g_assert(playlist_current() != playlist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_remove()
|
||||
{
|
||||
struct playlist *playlist;
|
||||
unsigned int i;
|
||||
|
||||
playlist_set_random(__test_pl_queued(), true);
|
||||
g_assert_true(playlist_sort(__test_pl_queued(), COMPARE_TRACK));
|
||||
|
||||
g_assert_true( playlist_remove(__test_pl_favorites(), track_get(0)));
|
||||
g_assert_false(playlist_remove(__test_pl_favorites(), track_get(0)));
|
||||
g_assert_true( playlist_remove(__test_pl_queued(), track_get(0)));
|
||||
g_assert_false(playlist_remove(__test_pl_queued(), track_get(0)));
|
||||
g_assert_false(playlist_remove(__test_pl_history(), track_get(0)));
|
||||
|
||||
g_assert_cmpuint(playlist_size(__test_pl_favorites()), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_queued()), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_history()), ==, 2);
|
||||
|
||||
g_assert_false(playlist_has(__test_pl_favorites(), track_get(0)));
|
||||
g_assert_false(playlist_has(__test_pl_queued(), track_get(0)));
|
||||
g_assert_true( playlist_has(__test_pl_history(), track_get(0)));
|
||||
|
||||
g_assert_true( playlist_remove(__test_pl_hidden(), track_get(0)));
|
||||
g_assert_true( playlist_remove(__test_pl_hidden(), track_get(1)));
|
||||
g_assert_true( playlist_remove(__test_pl_hidden(), track_get(2)));
|
||||
g_assert_false(playlist_remove(__test_pl_most_played(), track_get(0)));
|
||||
g_assert_false(playlist_remove(__test_pl_least_played(), track_get(1)));
|
||||
g_assert_false(playlist_remove(__test_pl_unplayed(), track_get(2)));
|
||||
|
||||
g_assert_cmpuint(playlist_size(__test_pl_hidden()), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_collection()), ==, 3);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_most_played()), ==, 1);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_least_played()), ==, 1);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_unplayed()), ==, 1);
|
||||
|
||||
g_assert_false(playlist_has(__test_pl_hidden(), track_get(0)));
|
||||
g_assert_false(playlist_has(__test_pl_hidden(), track_get(1)));
|
||||
g_assert_false(playlist_has(__test_pl_hidden(), track_get(2)));
|
||||
g_assert_true( playlist_has(__test_pl_collection(), track_get(0)));
|
||||
g_assert_true( playlist_has(__test_pl_collection(), track_get(1)));
|
||||
g_assert_true( playlist_has(__test_pl_collection(), track_get(2)));
|
||||
g_assert_true( playlist_has(__test_pl_most_played(), track_get(0)));
|
||||
g_assert_true( playlist_has(__test_pl_least_played(), track_get(1)));
|
||||
g_assert_true( playlist_has(__test_pl_unplayed(), track_get(2)));
|
||||
|
||||
g_assert_true(playlist_remove(__test_pl_collection(), track_get(0)));
|
||||
g_assert_cmpuint(playlist_size(__test_pl_hidden()), ==, 1);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_collection()), ==, 2);
|
||||
g_assert_true (playlist_has(__test_pl_hidden(), track_get(0)));
|
||||
g_assert_false(playlist_has(__test_pl_collection(), track_get(0)));
|
||||
g_assert_true(playlist_remove(__test_pl_hidden(), track_get(0)));
|
||||
|
||||
g_assert_true(playlist_select(__test_pl_least_played()));
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) {
|
||||
playlist = playlist_get(PL_SYSTEM, i);
|
||||
|
||||
switch (i) {
|
||||
case SYS_PL_QUEUED:
|
||||
g_assert_false(playlist->pl_random);
|
||||
g_assert_cmpuint(g_slist_length(playlist->pl_sort), ==, 0);
|
||||
case SYS_PL_FAVORITES:
|
||||
case SYS_PL_HIDDEN:
|
||||
case SYS_PL_HISTORY:
|
||||
g_assert_false(playlist_select(playlist));
|
||||
g_assert(playlist_current() != playlist);
|
||||
break;
|
||||
default:
|
||||
g_assert_true( playlist_select(playlist));
|
||||
g_assert_false(playlist_select(playlist));
|
||||
g_assert(playlist_current() == playlist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_delete()
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
g_assert_true(playlist_add(__test_pl_favorites(), track_get(0)));
|
||||
g_assert_true(playlist_add(__test_pl_hidden(), track_get(1)));
|
||||
g_assert_true(playlist_add(__test_pl_hidden(), track_get(2)));
|
||||
g_assert_true(playlist_add(__test_pl_queued(), track_get(0)));
|
||||
g_assert_true(playlist_add(__test_pl_history(), track_get(0)));
|
||||
|
||||
playlist_set_random(__test_pl_queued(), true);
|
||||
g_assert_true(playlist_sort(__test_pl_queued(), COMPARE_TRACK));
|
||||
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++)
|
||||
g_assert_false(playlist_delete(playlist_get(PL_SYSTEM, i)));
|
||||
|
||||
g_assert_cmpuint(playlist_size(__test_pl_favorites()), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_hidden()), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_queued()), ==, 0);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_collection()), ==, 3);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_history()), ==, 3);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_unplayed()), ==, 1);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_most_played()), ==, 1);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_least_played()), ==, 1);
|
||||
|
||||
g_assert_false(__test_pl_queued()->pl_random);
|
||||
g_assert_cmpuint(g_slist_length(__test_pl_queued()->pl_sort), ==, 0);
|
||||
}
|
||||
|
||||
static void test_next()
|
||||
{
|
||||
playlist_select(__test_pl_collection());
|
||||
g_assert_cmpuint(playlist_next()->tr_track, ==, 1);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_collection()), ==, 3);
|
||||
g_assert_cmpuint(playlist_next()->tr_track, ==, 2);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_collection()), ==, 3);
|
||||
g_assert_cmpuint(playlist_next()->tr_track, ==, 3);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_collection()), ==, 3);
|
||||
g_assert_cmpuint(playlist_next()->tr_track, ==, 1);
|
||||
g_assert_cmpuint(playlist_size(__test_pl_collection()), ==, 3);
|
||||
|
||||
playlist_add(__test_pl_queued(), track_get(0));
|
||||
playlist_add(__test_pl_queued(), track_get(1));
|
||||
g_assert_cmpuint(playlist_size(__test_pl_queued()), ==, 2);
|
||||
g_assert(playlist_current() == __test_pl_queued());
|
||||
|
||||
g_assert(playlist_next() == track_get(0));
|
||||
g_assert_cmpuint(playlist_size(__test_pl_queued()), ==, 2);
|
||||
playlist_selected(track_get(0));
|
||||
g_assert_cmpuint(playlist_size(__test_pl_queued()), ==, 1);
|
||||
g_assert(playlist_current() == __test_pl_queued());
|
||||
|
||||
g_assert(playlist_next() == track_get(1));
|
||||
g_assert_cmpuint(playlist_size(__test_pl_queued()), ==, 1);
|
||||
playlist_selected(track_get(1));
|
||||
g_assert_cmpuint(playlist_size(__test_pl_queued()), ==, 0);
|
||||
g_assert(playlist_current() == __test_pl_collection());
|
||||
|
||||
playlist_generic_clear(__test_pl_history());
|
||||
g_assert_cmpuint(playlist_size(__test_pl_history()), ==, 0);
|
||||
g_assert_true(playlist_add(__test_pl_history(), track_get(0)));
|
||||
g_assert_true(playlist_add(__test_pl_history(), track_get(1)));
|
||||
g_assert_true(playlist_add(__test_pl_history(), track_get(2)));
|
||||
g_assert_cmpuint(playlist_size(__test_pl_history()), ==, 3);
|
||||
g_assert(playlist_prev() == track_get(1));
|
||||
g_assert(playlist_prev() == track_get(0));
|
||||
g_assert(playlist_prev() == track_get(2));
|
||||
g_assert_cmpuint(playlist_size(__test_pl_history()), ==, 3);
|
||||
|
||||
}
|
||||
|
||||
static void test_delete_tracks()
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
g_assert_true(playlist_add(__test_pl_favorites(), track_get(0)));
|
||||
g_assert_true(playlist_add(__test_pl_hidden(), track_get(1)));
|
||||
g_assert_true(playlist_add(__test_pl_queued(), track_get(0)));
|
||||
|
||||
pl_system_delete_track(track_get(0));
|
||||
pl_system_delete_track(track_get(1));
|
||||
pl_system_delete_track(track_get(2));
|
||||
|
||||
for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++)
|
||||
g_assert_cmpuint(playlist_size(playlist_get(PL_SYSTEM, i)), ==, 0);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct library *library;
|
||||
int ret;
|
||||
|
||||
idle_init_sync();
|
||||
idle_init(IDLE_SYNC);
|
||||
settings_init();
|
||||
tags_init();
|
||||
playlist_init(NULL);
|
||||
while (idle_run_task()) {};
|
||||
|
||||
/* Add tracks to the collection. */
|
||||
library = library_find("tests/Music");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/01 - Title Theme.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/02 - Kokiri Forest.ogg");
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/Core/Playlists/System/Invalid", test_invalid);
|
||||
g_test_add_func("/Core/Playlists/System/Favorites", test_favorites);
|
||||
g_test_add_func("/Core/Playlists/System/Hidden", test_hidden);
|
||||
g_test_add_func("/Core/Playlists/System/Queued", test_queued);
|
||||
g_test_add_func("/Core/Playlists/System/Collection", test_collection);
|
||||
g_test_add_func("/Core/Playlists/System/History", test_history);
|
||||
g_test_add_func("/Core/Playlists/System/Unplayed Tracks", test_unplayed);
|
||||
g_test_add_func("/Core/Playlists/System/Most Played Tracks", test_most_played);
|
||||
g_test_add_func("/Core/Playlists/System/Least Played Tracks", test_least_played);
|
||||
g_test_add_func("/Core/Playlists/System/Init", test_init);
|
||||
g_test_add_func("/Core/Playlists/System/Random", test_random);
|
||||
g_test_add_func("/Core/Playlists/System/Sort", test_sort);
|
||||
g_test_add_func("/Core/Playlists/System/Played", test_played);
|
||||
g_test_add_func("/Core/Playlists/System/Add Tracks", test_add);
|
||||
g_test_add_func("/Core/Playlists/System/Remove Tracks", test_remove);
|
||||
g_test_add_func("/Core/Playlists/System/Delete", test_delete);
|
||||
g_test_add_func("/Core/Playlists/System/Next", test_next);
|
||||
g_test_add_func("/Core/PLaylists/System/Delete Tracks", test_delete_tracks);
|
||||
ret = g_test_run();
|
||||
|
||||
playlist_deinit();
|
||||
|
|
|
@ -12,51 +12,74 @@
|
|||
void test_user()
|
||||
{
|
||||
struct database *db = pl_user_db_get();
|
||||
struct playlist *playlist;
|
||||
|
||||
g_assert_cmpuint(db->db_size, ==, 0);
|
||||
g_assert_true( playlist_new(PL_USER, "Test Playlist"));
|
||||
g_assert_false(playlist_new(PL_USER, "Test Playlist"));
|
||||
playlist = playlist_new(PL_USER, "Test Playlist");
|
||||
g_assert_nonnull(playlist);
|
||||
g_assert_cmpuint(playlist->pl_id, ==, 0);
|
||||
g_assert_cmpuint(playlist->pl_length, ==, 0);
|
||||
g_assert_false(playlist->pl_random);
|
||||
|
||||
g_assert_null(playlist_new(PL_USER, "Test Playlist"));
|
||||
g_assert_cmpuint(db->db_size, ==, 1);
|
||||
|
||||
g_assert_false(playlist_get_random(PL_USER, "Test Playlist"));
|
||||
playlist_set_random(PL_USER, "Test Playlist", true);
|
||||
g_assert_true(playlist_get_random(PL_USER, "Test Playlist"));
|
||||
playlist_set_random(PL_USER, "Test Playlist", false);
|
||||
g_assert_false(playlist_get_random(PL_USER, "Test Playlist"));
|
||||
g_assert_false(playlist->pl_random);
|
||||
playlist_set_random(playlist, true);
|
||||
g_assert_true(playlist->pl_random);
|
||||
playlist_set_random(playlist, false);
|
||||
g_assert_false(playlist->pl_random);
|
||||
|
||||
g_assert_cmpuint(playlist_get_id(PL_USER, "Test Playlist"), ==, 0);
|
||||
g_assert_cmpuint(playlist_get_id(PL_USER, "No Playlist"), ==,
|
||||
(unsigned int)-1);
|
||||
g_assert(playlist_current() != playlist);
|
||||
g_assert_false(playlist_select(playlist));
|
||||
g_assert(playlist_current() != playlist);
|
||||
|
||||
g_assert_cmpstr_free(playlist_get_name(PL_USER, 0), ==, "Test Playlist");
|
||||
g_assert_null(playlist_get_name(PL_USER, 1));
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 0);
|
||||
g_assert_false(playlist_has(playlist, track_get(0)));
|
||||
g_assert_true( playlist_add(playlist, track_get(0)));
|
||||
g_assert_false(playlist_add(playlist, track_get(0)));
|
||||
g_assert_true( playlist_has(playlist, track_get(0)));
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 1);
|
||||
|
||||
g_assert_true( playlist_select(PL_USER, "Test Playlist"));
|
||||
g_assert_false(playlist_select(PL_USER, "No Playlist"));
|
||||
g_assert_true(playlist_select(playlist));
|
||||
g_assert(playlist_current() == playlist);
|
||||
g_assert_false(playlist_select(playlist));
|
||||
g_assert_false(playlist_select(NULL));
|
||||
|
||||
g_assert_cmpuint(playlist_size(PL_USER, "Test Playlist"), ==, 0);
|
||||
g_assert_false(playlist_has( PL_USER, "Test Playlist", track_get(0)));
|
||||
g_assert_true( playlist_add( PL_USER, "Test Playlist", track_get(0)));
|
||||
g_assert_false(playlist_add( PL_USER, "Test Playlist", track_get(0)));
|
||||
g_assert_true( playlist_has( PL_USER, "Test Playlist", track_get(0)));
|
||||
g_assert_cmpuint(playlist_size(PL_USER, "Test Playlist"), ==, 1);
|
||||
g_assert(playlist_next() == track_get(0));
|
||||
g_assert_true(playlist_has(playlist, track_get(0)));
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 1);
|
||||
|
||||
playlist_selected(track_get(0));
|
||||
playlist_played(track_get(0));
|
||||
|
||||
playlist_update(PL_USER, "Test Playlist");
|
||||
pl_user_deinit();
|
||||
g_assert_cmpuint(db->db_size, ==, 0);
|
||||
pl_user_init(NULL);
|
||||
pl_user_init();
|
||||
while (idle_run_task()) {};
|
||||
g_assert_cmpuint(db->db_size, ==, 1);
|
||||
|
||||
g_assert_cmpuint(playlist_size(PL_USER, "Test Playlist"), ==, 1);
|
||||
g_assert_true( playlist_has( PL_USER, "Test Playlist", track_get(0)));
|
||||
g_assert_true( playlist_remove(PL_USER, "Test Playlist", track_get(0)));
|
||||
g_assert_false(playlist_remove(PL_USER, "Test Playlist", track_get(0)));
|
||||
g_assert_false(playlist_has( PL_USER, "Test Playlist", track_get(0)));
|
||||
g_assert_cmpuint(playlist_size(PL_USER, "Test Playlist"), ==, 0);
|
||||
playlist = playlist_lookup(PL_USER, "Test Playlist");
|
||||
g_assert_nonnull(playlist);
|
||||
|
||||
g_assert_true( playlist_delete(PL_USER, "Test Playlist"));
|
||||
g_assert_false(playlist_delete(PL_USER, "Test Playlist"));
|
||||
g_assert_false(pl_user_rename(NULL, "Null Playlist"));
|
||||
g_assert_true( pl_user_rename(playlist, "New Playlist Name"));
|
||||
g_assert_null(playlist_lookup(PL_USER, "Test Playlist"));
|
||||
g_assert_cmpstr(playlist->pl_name, ==, "New Playlist Name");
|
||||
|
||||
g_assert_nonnull(playlist_new(PL_USER, "Test Playlist"));
|
||||
g_assert_false(pl_user_rename(playlist, "Test Playlist"));
|
||||
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 1);
|
||||
g_assert_true( playlist_has( playlist, track_get(0)));
|
||||
g_assert_true( playlist_remove(playlist, track_get(0)));
|
||||
g_assert_false(playlist_remove(playlist, track_get(0)));
|
||||
g_assert_false(playlist_has( playlist, track_get(0)));
|
||||
g_assert_cmpuint(playlist_size(playlist), ==, 0);
|
||||
|
||||
g_assert_true(playlist_delete(playlist));
|
||||
playlist = playlist_lookup(PL_USER, "Test Playlist");
|
||||
g_assert_true(playlist_delete(playlist));
|
||||
g_assert_cmpuint(db->db_size, ==, 0);
|
||||
g_assert_cmpuint(db_actual_size(db), ==, 0);
|
||||
}
|
||||
|
@ -65,7 +88,7 @@ int main(int argc, char **argv)
|
|||
{
|
||||
int ret;
|
||||
|
||||
idle_init_sync();
|
||||
idle_init(IDLE_SYNC);
|
||||
settings_init();
|
||||
tags_init();
|
||||
playlist_init(NULL);
|
||||
|
|
|
@ -1,525 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/idle.h>
|
||||
#include <core/queue.h>
|
||||
#include <core/tags/tags.h>
|
||||
#include <tests/test.h>
|
||||
|
||||
|
||||
unsigned int count_init = 0;
|
||||
unsigned int count_deinit = 0;
|
||||
unsigned int count_added = 0;
|
||||
unsigned int count_erase = 0;
|
||||
unsigned int count_deleted = 0;
|
||||
unsigned int count_cleared = 0;
|
||||
unsigned int count_flags = 0;
|
||||
unsigned int count_sort = 0;
|
||||
unsigned int count_updated = 0;
|
||||
|
||||
bool can_erase = true;
|
||||
|
||||
|
||||
static void *queue_op_init(struct queue *queue, void *data)
|
||||
{
|
||||
count_init++;
|
||||
return GUINT_TO_POINTER(count_init);
|
||||
}
|
||||
|
||||
static void queue_op_deinit(struct queue *queue)
|
||||
{
|
||||
count_deinit++;
|
||||
}
|
||||
|
||||
static void queue_op_added(struct queue *queue, unsigned int pos)
|
||||
{
|
||||
count_added++;
|
||||
}
|
||||
|
||||
static bool queue_op_erase(struct queue *queue, struct track *track)
|
||||
{
|
||||
count_erase++;
|
||||
return can_erase;
|
||||
}
|
||||
|
||||
static void queue_op_removed(struct queue *queue, unsigned int pos)
|
||||
{
|
||||
count_deleted++;
|
||||
}
|
||||
|
||||
static void queue_op_cleared(struct queue *queue, unsigned int n)
|
||||
{
|
||||
count_cleared++;
|
||||
}
|
||||
|
||||
static void queue_op_save(struct queue *queue, enum queue_flags flag)
|
||||
{
|
||||
if (flag == Q_SAVE_FLAGS)
|
||||
count_flags++;
|
||||
else if (flag == Q_SAVE_SORT)
|
||||
count_sort++;
|
||||
}
|
||||
|
||||
static void queue_op_updated(struct queue *queue, unsigned int pos)
|
||||
{
|
||||
count_updated++;
|
||||
}
|
||||
|
||||
|
||||
static const struct queue_ops test_ops = {
|
||||
.qop_init = queue_op_init,
|
||||
.qop_deinit = queue_op_deinit,
|
||||
.qop_added = queue_op_added,
|
||||
.qop_erase = queue_op_erase,
|
||||
.qop_removed = queue_op_removed,
|
||||
.qop_cleared = queue_op_cleared,
|
||||
.qop_save = queue_op_save,
|
||||
.qop_updated = queue_op_updated,
|
||||
};
|
||||
|
||||
|
||||
static void test_init()
|
||||
{
|
||||
struct queue q;
|
||||
struct queue_iter it;
|
||||
|
||||
queue_init(&q, 0, NULL, NULL);
|
||||
g_assert_cmpuint(count_init, ==, 0);
|
||||
g_assert_null(q.q_private);
|
||||
|
||||
g_assert_cmpuint(q.q_cur.it_pos, ==, (unsigned int)-1);
|
||||
g_assert_cmpuint(q.q_flags, ==, 0);
|
||||
g_assert_cmpuint(q.q_length, ==, 0);
|
||||
g_assert_null(q.q_sort);
|
||||
g_assert_null(q.q_ops);
|
||||
g_assert_null(queue_next(&q));
|
||||
|
||||
queue_iter_init(&q, &it);
|
||||
g_assert_null(it.it_iter);
|
||||
g_assert_cmpuint(it.it_pos, ==, (unsigned int)-1);
|
||||
g_assert_null(queue_iter_val(&it));
|
||||
|
||||
queue_deinit(&q);
|
||||
g_assert_cmpuint(count_deinit, ==, 0);
|
||||
|
||||
queue_init(&q, Q_ENABLED | Q_RANDOM, &test_ops, NULL);
|
||||
g_assert_cmpuint(count_init, ==, 1);
|
||||
g_assert_cmpuint(GPOINTER_TO_UINT(q.q_private), ==, 1);
|
||||
|
||||
g_assert_cmpuint(q.q_cur.it_pos, ==, (unsigned int)-1);
|
||||
g_assert_cmpuint(q.q_flags, ==, Q_ENABLED | Q_RANDOM);
|
||||
g_assert_cmpuint(q.q_length, ==, 0);
|
||||
g_assert_null(q.q_sort);
|
||||
g_assert(q.q_ops == &test_ops);
|
||||
g_assert_null(queue_next(&q));
|
||||
|
||||
queue_deinit(&q);
|
||||
g_assert_cmpuint(count_deinit, ==, 1);
|
||||
}
|
||||
|
||||
static void test_flags()
|
||||
{
|
||||
struct queue q;
|
||||
|
||||
queue_init(&q, 0, &test_ops, NULL);
|
||||
g_assert_cmpuint(q.q_flags, ==, 0);
|
||||
g_assert_false(queue_has_flag(&q, Q_ENABLED));
|
||||
g_assert_false(queue_has_flag(&q, Q_RANDOM));
|
||||
g_assert_false(queue_has_flag(&q, Q_REPEAT));
|
||||
g_assert_false(queue_has_flag(&q, Q_NO_SORT));
|
||||
g_assert_false(queue_has_flag(&q, Q_SAVE_FLAGS));
|
||||
g_assert_false(queue_has_flag(&q, Q_SAVE_SORT));
|
||||
g_assert_false(queue_has_flag(&q, Q_ADD_FRONT));
|
||||
|
||||
queue_set_flag(&q, Q_ENABLED);
|
||||
g_assert_cmpuint(q.q_flags, ==, Q_ENABLED);
|
||||
g_assert_cmpuint(count_flags, ==, 0);
|
||||
|
||||
queue_unset_flag(&q, Q_ENABLED);
|
||||
g_assert_cmpuint(q.q_flags, ==, 0);
|
||||
g_assert_cmpuint(count_flags, ==, 0);
|
||||
|
||||
queue_set_flag(&q, Q_SAVE_FLAGS);
|
||||
queue_set_flag(&q, Q_ENABLED);
|
||||
queue_set_flag(&q, Q_RANDOM);
|
||||
queue_set_flag(&q, Q_REPEAT);
|
||||
queue_set_flag(&q, Q_NO_SORT);
|
||||
queue_set_flag(&q, Q_ADD_FRONT);
|
||||
g_assert_true(queue_has_flag(&q, Q_ENABLED));
|
||||
g_assert_true(queue_has_flag(&q, Q_RANDOM));
|
||||
g_assert_true(queue_has_flag(&q, Q_REPEAT));
|
||||
g_assert_true(queue_has_flag(&q, Q_NO_SORT));
|
||||
g_assert_true(queue_has_flag(&q, Q_SAVE_FLAGS));
|
||||
g_assert_true(queue_has_flag(&q, Q_ADD_FRONT));
|
||||
g_assert_cmpuint(count_flags, ==, 6);
|
||||
|
||||
queue_unset_flag(&q, Q_ENABLED);
|
||||
queue_unset_flag(&q, Q_RANDOM);
|
||||
queue_unset_flag(&q, Q_REPEAT);
|
||||
queue_unset_flag(&q, Q_NO_SORT);
|
||||
queue_unset_flag(&q, Q_ADD_FRONT);
|
||||
queue_unset_flag(&q, Q_SAVE_FLAGS);
|
||||
g_assert_cmpuint(q.q_flags, ==, 0);
|
||||
g_assert_cmpuint(count_flags, ==, 11);
|
||||
}
|
||||
|
||||
static void test_queue(gconstpointer arg)
|
||||
{
|
||||
unsigned int N = GPOINTER_TO_UINT(arg);
|
||||
unsigned int ex_length = 0;
|
||||
unsigned int ex_size = N;
|
||||
struct queue_iter it;
|
||||
struct track *track;
|
||||
unsigned int i;
|
||||
struct queue q;
|
||||
|
||||
count_added = 0;
|
||||
count_deleted = 0;
|
||||
count_cleared = 0;
|
||||
count_updated = 0;
|
||||
|
||||
queue_init(&q, 0, &test_ops, NULL);
|
||||
|
||||
/* queue_add() */
|
||||
for (i = 0; i < N; i++) {
|
||||
track = track_get(i % 13);
|
||||
ex_length += track->tr_length;
|
||||
g_assert_cmpuint(queue_add(&q, track), ==, i);
|
||||
g_assert_cmpuint(count_added, ==, i + 1);
|
||||
}
|
||||
g_assert_cmpuint(q.q_length, ==, ex_length);
|
||||
g_assert_cmpuint(queue_size(&q), ==, ex_size);
|
||||
|
||||
/* queue_iter_init() */
|
||||
if (N > 0) {
|
||||
queue_iter_init(&q, &it);
|
||||
g_assert_nonnull(it.it_iter);
|
||||
g_assert_cmpuint(it.it_pos, ==, 0);
|
||||
}
|
||||
|
||||
/* queue_for_each() */
|
||||
i = 0;
|
||||
queue_for_each(&q, &it) {
|
||||
g_assert_cmpuint(it.it_pos, ==, i);
|
||||
g_assert(queue_iter_val(&it) == track_get(i % 13));
|
||||
i++;
|
||||
}
|
||||
g_assert_cmpuint(i, ==, N);
|
||||
g_assert_cmpuint(queue_size(&q), ==, ex_size);
|
||||
|
||||
/* queue_remove_all() and queue_has() */
|
||||
track = track_get(0);
|
||||
ex_length -= track->tr_length * (N / 13);
|
||||
ex_size -= (N / 13);
|
||||
if (N > 0)
|
||||
g_assert_true(queue_has(&q, track));
|
||||
else
|
||||
g_assert_false(queue_has(&q, track));
|
||||
g_assert_cmpuint(queue_remove_all(&q, track), ==, N / 13);
|
||||
g_assert_cmpuint(q.q_length, ==, ex_length);
|
||||
g_assert_cmpuint(queue_size(&q), ==, ex_size);
|
||||
g_assert_false(queue_has(&q, track));
|
||||
|
||||
/* queue_erase() = false */
|
||||
can_erase = false;
|
||||
for (i = 0; i < ex_size; i += 11) {
|
||||
queue_erase(&q, i);
|
||||
g_assert_cmpuint(q.q_length, ==, ex_length);
|
||||
g_assert_cmpuint(queue_size(&q), ==, ex_size);
|
||||
}
|
||||
|
||||
/* queue_remove() and queue_erase() == true */
|
||||
can_erase = true;
|
||||
track = track_get(1);
|
||||
ex_length -= track->tr_length * (N / 13);
|
||||
ex_size -= (N / 13);
|
||||
for (i = 0; i < ex_size; i += 11) {
|
||||
g_assert(queue_at(&q, i) == track);
|
||||
if (i % 2 == 0)
|
||||
queue_remove(&q, i);
|
||||
else
|
||||
queue_erase(&q, i);
|
||||
}
|
||||
g_assert_cmpuint(q.q_length, ==, ex_length);
|
||||
g_assert_cmpuint(queue_size(&q), ==, ex_size);
|
||||
|
||||
/* queue_updated() */
|
||||
track = track_get(2);
|
||||
queue_updated(&q, track);
|
||||
g_assert_cmpuint(count_updated, ==, N / 13);
|
||||
|
||||
g_assert_null(queue_next(&q));
|
||||
g_assert_cmpint(queue_size(&q), ==, ex_size);
|
||||
|
||||
/* Tracks should not be removed. */
|
||||
queue_set_flag(&q, Q_ENABLED);
|
||||
queue_set_flag(&q, Q_REPEAT);
|
||||
for (i = 0; i < ex_size; i++) {
|
||||
g_assert(queue_next(&q) == track_get((i % 11) + 2));
|
||||
g_assert_cmpuint(count_updated, ==, (N / 13) + (2 * i) + 1);
|
||||
queue_selected(&q, i);
|
||||
g_assert_cmpuint(count_updated, ==, (N / 13) + (2 * i) + 2);
|
||||
g_assert_cmpuint(queue_size(&q), ==, ex_size);
|
||||
}
|
||||
|
||||
/* Tracks should be removed. */
|
||||
queue_unset_flag(&q, Q_REPEAT);
|
||||
for (i = 0; i < ex_size / 2; i++) {
|
||||
track = queue_next(&q);
|
||||
ex_length -= track->tr_length;
|
||||
ex_size--;
|
||||
|
||||
g_assert(track == track_get((i % 11) + 2));
|
||||
g_assert_cmpuint(queue_size(&q), ==, ex_size);
|
||||
g_assert_cmpuint(q.q_length, ==, ex_length);
|
||||
}
|
||||
|
||||
queue_clear(&q);
|
||||
g_assert_cmpuint(count_cleared, ==, 1);
|
||||
g_assert_cmpuint(queue_size(&q), ==, 0);
|
||||
g_assert_cmpuint(q.q_length, ==, 0);
|
||||
|
||||
queue_deinit(&q);
|
||||
g_assert_cmpuint(count_cleared, ==, 2);
|
||||
g_assert_null(q.q_sort);
|
||||
}
|
||||
|
||||
static void test_rand_select()
|
||||
{
|
||||
unsigned int i;
|
||||
struct queue q;
|
||||
|
||||
g_random_set_seed(0);
|
||||
queue_init(&q, Q_ENABLED | Q_RANDOM, &test_ops, NULL);
|
||||
|
||||
/* Call next() on an empty queue. */
|
||||
for (i = 0; i < 13; i++) {
|
||||
g_assert_null(queue_next(&q));
|
||||
g_assert_cmpuint(queue_size(&q), ==, 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < 13; i++)
|
||||
queue_add(&q, track_get(i));
|
||||
queue_sort(&q, COMPARE_TRACK, true);
|
||||
|
||||
/*
|
||||
* The comments below use the following notation:
|
||||
* <val>: The value pointed to by q._cur.
|
||||
* (val): The value selected by q.track_selected().
|
||||
* [val]: The value picked by q.next().
|
||||
*/
|
||||
|
||||
/* rand() = 9, q = { <>, 1, 2, 3, 4, 5, 6, 7, 8, [9], 10, 11, 12, 13 } */
|
||||
g_assert_cmpuint(queue_next(&q)->tr_track, ==, 9);
|
||||
g_assert_cmpuint(queue_size(&q), ==, 12);
|
||||
|
||||
/* select = 6, q = { 1, 2, 3, 4, 5, 6, (7), <8>, 10, 11, 12, 13 } */
|
||||
g_assert_cmpuint(queue_selected(&q, 6)->tr_track, ==, 7);
|
||||
g_assert_cmpuint(queue_size(&q), ==, 11);
|
||||
|
||||
/* rand() = 10, q = { 1, 2, 3, 4, [5], <6>, 8, 10, 11, 12, 13 } */
|
||||
g_assert_cmpuint(queue_next(&q)->tr_track, ==, 5);
|
||||
g_assert_cmpuint(queue_size(&q), ==, 10);
|
||||
|
||||
/* select = 7, q = { 1, 2, 3, <4>, 6, 8, 10, (11), 12, 13 } */
|
||||
g_assert_cmpuint(queue_selected(&q, 7)->tr_track, ==, 11);
|
||||
g_assert_cmpuint(queue_size(&q), ==, 9);
|
||||
|
||||
/* rand() = 6, q = { 1, 2, 3, [4], 6, 8, <10>, 12, 13 } */
|
||||
g_assert_cmpuint(queue_next(&q)->tr_track, ==, 4);
|
||||
g_assert_cmpuint(queue_size(&q), ==, 8);
|
||||
|
||||
/* select = 2, q = { 1, 2, (<3>), 6, 8, 10, 12, 13 } */
|
||||
g_assert_cmpuint(queue_selected(&q, 2)->tr_track, ==, 3);
|
||||
g_assert_cmpuint(queue_size(&q), ==, 7);
|
||||
|
||||
/* rand() = 1, q = { 1, <2>, [6], 8, 10, 12, 13 } */
|
||||
g_assert_cmpuint(queue_next(&q)->tr_track, ==, 6);
|
||||
g_assert_cmpuint(queue_size(&q), ==, 6);
|
||||
|
||||
/* select = 1, q = { 1, (<2>), 8, 10, 12, 13 } */
|
||||
g_assert_cmpuint(queue_selected(&q, 1)->tr_track, ==, 2);
|
||||
g_assert_cmpuint(queue_size(&q), ==, 5);
|
||||
|
||||
/* rand() = 4, q = { <1>, 8, 10, 12, [13] } */
|
||||
g_assert_cmpuint(queue_next(&q)->tr_track, ==, 13);
|
||||
g_assert_cmpuint(queue_size(&q), ==, 4);
|
||||
|
||||
/* rand() = 1, q = { [1], 8, 10, <12> } */
|
||||
g_assert_cmpuint(queue_next(&q)->tr_track, ==, 1);
|
||||
g_assert_cmpuint(queue_size(&q), ==, 3);
|
||||
|
||||
/* select = 1, q = { <>, 8, (10), 12 } */
|
||||
g_assert_cmpuint(queue_selected(&q, 1)->tr_track, ==, 10);
|
||||
g_assert_cmpuint(queue_size(&q), ==, 2);
|
||||
|
||||
/* rand() = 1, q = { <8>, [12] } */
|
||||
g_assert_cmpuint(queue_next(&q)->tr_track, ==, 12);
|
||||
g_assert_cmpuint(queue_size(&q), ==, 1);
|
||||
|
||||
/* select = 0, q = { (<8>) } */
|
||||
g_assert_cmpuint(queue_selected(&q, 0)->tr_track, ==, 8);
|
||||
g_assert_cmpuint(queue_size(&q), ==, 0);
|
||||
|
||||
/* q = { } */
|
||||
g_assert_null(queue_next(&q));
|
||||
g_assert_null(queue_selected(&q, 3));
|
||||
|
||||
queue_deinit(&q);
|
||||
}
|
||||
|
||||
static void test_sorting()
|
||||
{
|
||||
unsigned int ex_count[] = { 7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6 };
|
||||
unsigned int ex_title[] = { 7, 10, 9, 4, 3, 6, 2, 5, 12, 11, 13, 1, 8 };
|
||||
unsigned int ex_co_ti[] = { 4, 3, 6, 2, 5, 1, 7, 10, 9, 12, 11, 13, 8 };
|
||||
struct track *track;
|
||||
unsigned int i;
|
||||
struct queue q;
|
||||
|
||||
queue_init(&q, Q_SAVE_SORT | Q_ADD_FRONT, &test_ops, NULL);
|
||||
for (i = 0; i < 13; i++) {
|
||||
track = track_get(i);
|
||||
track->tr_count = (track->tr_track <= 6) ? 4 : 2;
|
||||
queue_add(&q, track);
|
||||
}
|
||||
|
||||
for (i = 0; i < 13; i++) {
|
||||
track = queue_at(&q, i);
|
||||
g_assert_nonnull(track);
|
||||
g_assert_cmpuint(track->tr_dbe.dbe_index, ==, 12 - i);
|
||||
}
|
||||
g_assert_cmpuint(count_sort, ==, 0);
|
||||
|
||||
queue_sort(&q, COMPARE_TRACK, true);
|
||||
for (i = 0; i < 13; i++) {
|
||||
track = queue_at(&q, i);
|
||||
g_assert_nonnull(track);
|
||||
g_assert_cmpuint(track->tr_track, ==, i + 1);
|
||||
}
|
||||
g_assert_cmpuint(count_sort, ==, 1);
|
||||
|
||||
queue_sort(&q, COMPARE_COUNT, true);
|
||||
for (i = 0; i < 13; i++) {
|
||||
track = queue_at(&q, i);
|
||||
g_assert_nonnull(track);
|
||||
g_assert_cmpuint(track->tr_track, ==, ex_count[i]);
|
||||
}
|
||||
g_assert_cmpuint(count_sort, ==, 2);
|
||||
|
||||
queue_set_flag(&q, Q_NO_SORT);
|
||||
queue_sort(&q, COMPARE_TITLE, true);
|
||||
for (i = 0; i < 13; i++) {
|
||||
track = queue_at(&q, i);
|
||||
g_assert_nonnull(track);
|
||||
g_assert_cmpuint(track->tr_track, ==, ex_count[i]);
|
||||
}
|
||||
g_assert_cmpuint(count_sort, ==, 2);
|
||||
|
||||
queue_unset_flag(&q, Q_NO_SORT);
|
||||
queue_sort(&q, COMPARE_TITLE, true);
|
||||
for (i = 0; i < 13; i++) {
|
||||
track = queue_at(&q, i);
|
||||
g_assert_nonnull(track);
|
||||
g_assert_cmpuint(track->tr_track, ==, ex_title[i]);
|
||||
}
|
||||
g_assert_cmpuint(count_sort, ==, 3);
|
||||
|
||||
queue_sort(&q, COMPARE_COUNT, true);
|
||||
queue_sort(&q, COMPARE_TITLE, false);
|
||||
queue_sort(&q, COMPARE_COUNT, false);
|
||||
for (i = 0; i < 13; i++) {
|
||||
track = queue_at(&q, i);
|
||||
g_assert_nonnull(track);
|
||||
g_assert_cmpuint(track->tr_track, ==, ex_co_ti[i]);
|
||||
}
|
||||
g_assert_cmpuint(count_sort, ==, 6);
|
||||
|
||||
queue_unset_flag(&q, Q_SAVE_SORT);
|
||||
queue_sort(&q, COMPARE_ARTIST, true);
|
||||
queue_sort(&q, COMPARE_ALBUM, false);
|
||||
queue_sort(&q, COMPARE_TRACK, false);
|
||||
queue_sort(&q, COMPARE_TRACK, false);
|
||||
for (i = 0; i < 13; i++) {
|
||||
track = queue_at(&q, i);
|
||||
g_assert_nonnull(track);
|
||||
g_assert_cmpuint(track->tr_track, ==, 13 - i);
|
||||
}
|
||||
g_assert_cmpuint(count_sort, ==, 6);
|
||||
|
||||
queue_deinit(&q);
|
||||
g_assert_cmpuint(q.q_length, ==, 0);
|
||||
g_assert_cmpuint(queue_size(&q),==, 0);
|
||||
g_assert_null(q.q_sort);
|
||||
}
|
||||
|
||||
static void test_save_load()
|
||||
{
|
||||
struct file f = FILE_INIT("queue.q", 0);
|
||||
struct queue q, r;
|
||||
unsigned int i;
|
||||
|
||||
queue_init(&q, 0, &test_ops, NULL);
|
||||
queue_init(&r, 0, &test_ops, NULL);
|
||||
|
||||
for (i = 0; i < 13; i++)
|
||||
queue_add(&q, track_get(i));
|
||||
|
||||
g_assert_false(file_exists(&f));
|
||||
g_assert_true(file_open(&f, OPEN_WRITE));
|
||||
queue_save_tracks(&q, &f);
|
||||
file_close(&f);
|
||||
g_assert_true(file_exists(&f));
|
||||
|
||||
g_assert_true(file_open(&f, OPEN_READ));
|
||||
queue_load_tracks(&r, &f);
|
||||
file_close(&f);
|
||||
|
||||
g_assert_cmpuint(queue_size(&r), ==, 13);
|
||||
for (i = 0; i < 13; i++)
|
||||
g_assert_true(queue_has(&r, track_get(i)));
|
||||
|
||||
queue_deinit(&q);
|
||||
queue_deinit(&r);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct library *library;
|
||||
int ret;
|
||||
|
||||
idle_init_sync();
|
||||
tags_init();
|
||||
while (idle_run_task()) {}
|
||||
|
||||
library = library_find("tests/Music");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/01 - Title Theme.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/02 - Kokiri Forest.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/03 - Hyrule Field.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/04 - Hyrule Castle.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/05 - Lon Lon Ranch.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/06 - Kakariko Village.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/07 - Death Mountain.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/08 - Zora's Domain.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/09 - Gerudo Valley.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/10 - Ganondorf.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/11 - Princess Zelda.ogg");
|
||||
track_add(library, "tests/Music/Hyrule Symphony/12 - Ocarina Medley.ogg");
|
||||
track_add(library,
|
||||
"tests/Music/Hyrule Symphony/13 - The Legend of Zelda Medley.ogg");
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/Core/Queue/Initialization", test_init);
|
||||
g_test_add_func("/Core/Queue/Flags", test_flags);
|
||||
g_test_add_data_func("/Core/Queue/n = 0", GUINT_TO_POINTER( 0), test_queue);
|
||||
g_test_add_data_func("/Core/Queue/n = 13", GUINT_TO_POINTER( 13), test_queue);
|
||||
g_test_add_data_func("/Core/Queue/n = 100,009)", GUINT_TO_POINTER(100009), test_queue);
|
||||
g_test_add_func("/Core/Queue/Random Next and Selection", test_rand_select);
|
||||
g_test_add_func("/Core/Queue/Sorting", test_sorting);
|
||||
g_test_add_func("/Core/Queue/Save and Load", test_save_load);
|
||||
ret = g_test_run();
|
||||
|
||||
tags_deinit();
|
||||
idle_deinit();
|
||||
return ret;
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
static void test_settings()
|
||||
{
|
||||
struct file f = FILE_INIT("settings", 0);
|
||||
struct file f = FILE_INIT_DATA("", "settings", 0);
|
||||
|
||||
settings_set(NULL, 0);
|
||||
g_assert_cmpuint(settings_get(NULL), ==, 0);
|
||||
|
|
|
@ -108,6 +108,18 @@ void test_match_tokens()
|
|||
g_assert_false(string_match_token("rule", tokens));
|
||||
}
|
||||
|
||||
void test_subdirectory()
|
||||
{
|
||||
g_assert_false(string_is_subdir(NULL, NULL));
|
||||
g_assert_false(string_is_subdir(NULL, "/a/b"));
|
||||
g_assert_false(string_is_subdir("/a/b", NULL));
|
||||
g_assert_false(string_is_subdir("/a", "/a/b"));
|
||||
g_assert_false(string_is_subdir("/a", "/ab"));
|
||||
g_assert_true( string_is_subdir("/a/b", "/a"));
|
||||
g_assert_true( string_is_subdir("/a/b", "/a/b"));
|
||||
g_assert_true( string_is_subdir("/a/b/c/d", "/a/b"));
|
||||
}
|
||||
|
||||
void test_length()
|
||||
{
|
||||
g_assert_cmpint(string_length(NULL), ==, 0);
|
||||
|
@ -125,6 +137,7 @@ int main(int argc, char **argv)
|
|||
g_test_add_func("/Core/String/Comparison/Tokens", test_compare_tokens);
|
||||
g_test_add_func("/Core/String/Matching", test_match);
|
||||
g_test_add_func("/Core/String/Matching/Tokens", test_match_tokens);
|
||||
g_test_add_func("/Core/String/Subdirectory", test_subdirectory);
|
||||
g_test_add_func("/Core/String/Length", test_length);
|
||||
return g_test_run();
|
||||
}
|
||||
|
|
|
@ -46,19 +46,18 @@ static void test_album()
|
|||
struct album *album;
|
||||
struct artist *koji;
|
||||
struct genre *genre;
|
||||
struct file f;
|
||||
struct file f = FILE_INIT_DATA("", "album_tag", 1);
|
||||
|
||||
idle_init();
|
||||
idle_init(IDLE_SYNC);
|
||||
|
||||
koji = artist_find("Koji Kondo");
|
||||
genre = genre_find("Video Game Music");
|
||||
album = ALBUM(album_ops->dbe_alloc("0/0/1998/Hyrule Symphony"));
|
||||
album = ALBUM(album_ops->dbe_alloc("0/0/1998/Hyrule Symphony", 0));
|
||||
test_verify_hyrule(album, koji, genre);
|
||||
g_assert_true( album_match_token(album, "hyrule"));
|
||||
g_assert_true( album_match_token(album, "symphony"));
|
||||
g_assert_false(album_match_token(album, "skyward"));
|
||||
|
||||
file_init(&f, "album_tag", 1);
|
||||
file_open(&f, OPEN_WRITE);
|
||||
file_writef(&f, "0 0 0 \n");
|
||||
album_ops->dbe_write(&f, &album->al_dbe);
|
||||
|
@ -66,11 +65,11 @@ static void test_album()
|
|||
album_ops->dbe_free(&album->al_dbe);
|
||||
|
||||
file_open(&f, OPEN_READ);
|
||||
album = ALBUM(album_ops->dbe_read(&f));
|
||||
album = ALBUM(album_ops->dbe_read(&f, 0));
|
||||
test_verify_empty(album);
|
||||
album_ops->dbe_free(&album->al_dbe);
|
||||
|
||||
album = ALBUM(album_ops->dbe_read(&f));
|
||||
album = ALBUM(album_ops->dbe_read(&f, 0));
|
||||
file_close(&f);
|
||||
test_verify_hyrule(album, koji, genre);
|
||||
album_ops->dbe_free(&album->al_dbe);
|
||||
|
@ -81,8 +80,8 @@ static void test_album_compare()
|
|||
const struct db_ops *album_ops = test_album_ops();
|
||||
struct album *twilight, *skyward;
|
||||
|
||||
twilight = ALBUM(album_ops->dbe_alloc("2006/Twilight Princess"));
|
||||
skyward = ALBUM(album_ops->dbe_alloc("2011/skyward sword"));
|
||||
twilight = ALBUM(album_ops->dbe_alloc("2006/Twilight Princess", 0));
|
||||
skyward = ALBUM(album_ops->dbe_alloc("2011/skyward sword", 0));
|
||||
|
||||
g_assert_cmpint(album_compare(twilight, twilight), ==, 0);
|
||||
g_assert_cmpint(album_compare(twilight, skyward), ==, 1);
|
||||
|
@ -134,7 +133,7 @@ static void test_album_artwork()
|
|||
const gchar *cache;
|
||||
|
||||
idle_deinit();
|
||||
idle_init();
|
||||
idle_init(IDLE_ASYNC);
|
||||
|
||||
cache = g_get_user_cache_dir();
|
||||
o_path = g_build_filename(cache, "ocarina-test", "core", "tags", "album", "1998", "Ocarina of Time.jpg", NULL);
|
||||
|
@ -182,7 +181,7 @@ int main(int argc, char **argv)
|
|||
{
|
||||
int ret;
|
||||
|
||||
idle_init_sync();
|
||||
idle_init(IDLE_SYNC);
|
||||
artist_db_init();
|
||||
genre_db_init();
|
||||
album_db_init();
|
||||
|
|
|
@ -28,19 +28,17 @@ static void test_verify_koji(struct artist *artist)
|
|||
|
||||
static void test_artist()
|
||||
{
|
||||
struct file f = FILE_INIT_DATA("", "artist_tag", 0);
|
||||
const struct db_ops *artist_ops = test_artist_ops();
|
||||
struct artist *artist;
|
||||
unsigned int i;
|
||||
struct file f;
|
||||
|
||||
artist = ARTIST(artist_ops->dbe_alloc("Koji Kondo"));
|
||||
artist = ARTIST(artist_ops->dbe_alloc("Koji Kondo", 0));
|
||||
|
||||
test_verify_koji(artist);
|
||||
g_assert_true( artist_match_token(artist, "koji"));
|
||||
g_assert_true( artist_match_token(artist, "kondo"));
|
||||
g_assert_false(artist_match_token(artist, "hajime"));
|
||||
|
||||
file_init(&f, "artist_tag", 0);
|
||||
file_open(&f, OPEN_WRITE);
|
||||
file_writef(&f, "1 \n2 ");
|
||||
artist_ops->dbe_write(&f, &artist->ar_dbe);
|
||||
|
@ -49,14 +47,14 @@ static void test_artist()
|
|||
artist_ops->dbe_free(&artist->ar_dbe);
|
||||
|
||||
file_open(&f, OPEN_READ);
|
||||
file_readf(&f, "%u", &i);
|
||||
artist = ARTIST(artist_ops->dbe_read(&f));
|
||||
file_readu(&f);
|
||||
artist = ARTIST(artist_ops->dbe_read(&f, 0));
|
||||
test_verify_empty(artist);
|
||||
g_free(artist->ar_name);
|
||||
artist_ops->dbe_free(&artist->ar_dbe);
|
||||
|
||||
file_readf(&f, "%u", &i);
|
||||
artist = ARTIST(artist_ops->dbe_read(&f));
|
||||
file_readu(&f);
|
||||
artist = ARTIST(artist_ops->dbe_read(&f, 0));
|
||||
file_close(&f);
|
||||
test_verify_koji(artist);
|
||||
g_free(artist->ar_name);
|
||||
|
@ -68,8 +66,8 @@ static void test_artist_compare()
|
|||
const struct db_ops *artist_ops = test_artist_ops();
|
||||
struct artist *koji, *hajime;
|
||||
|
||||
koji = ARTIST(artist_ops->dbe_alloc("Koji Kondo"));
|
||||
hajime = ARTIST(artist_ops->dbe_alloc("hajime wakai"));
|
||||
koji = ARTIST(artist_ops->dbe_alloc("Koji Kondo", 0));
|
||||
hajime = ARTIST(artist_ops->dbe_alloc("hajime wakai", 0));
|
||||
|
||||
g_assert_cmpint(artist_compare(koji, koji), ==, 0);
|
||||
g_assert_cmpint(artist_compare(koji, hajime), ==, 1);
|
||||
|
|
|
@ -27,18 +27,16 @@ static void test_verify_vg(struct genre *genre)
|
|||
|
||||
static void test_genre()
|
||||
{
|
||||
struct file f = FILE_INIT_DATA("", "genre_tag", 0);
|
||||
const struct db_ops *genre_ops = test_genre_ops();
|
||||
struct genre *genre;
|
||||
unsigned int i;
|
||||
struct file f;
|
||||
|
||||
genre = GENRE(genre_ops->dbe_alloc("Video Game Music"));
|
||||
genre = GENRE(genre_ops->dbe_alloc("Video Game Music", 0));
|
||||
test_verify_vg(genre);
|
||||
g_assert_true( genre_match_token(genre, "video"));
|
||||
g_assert_true( genre_match_token(genre, "music"));
|
||||
g_assert_false(genre_match_token(genre, "rock"));
|
||||
|
||||
file_init(&f, "genre_tag", 0);
|
||||
file_open(&f, OPEN_WRITE);
|
||||
file_writef(&f, "1 \n1 ");
|
||||
genre_ops->dbe_write(&f, &genre->ge_dbe);
|
||||
|
@ -47,14 +45,14 @@ static void test_genre()
|
|||
genre_ops->dbe_free(&genre->ge_dbe);
|
||||
|
||||
file_open(&f, OPEN_READ);
|
||||
file_readf(&f, "%u", &i);
|
||||
genre = GENRE(genre_ops->dbe_read(&f));
|
||||
file_readu(&f);
|
||||
genre = GENRE(genre_ops->dbe_read(&f, 0));
|
||||
test_verify_empty(genre);
|
||||
g_free(genre->ge_name);
|
||||
genre_ops->dbe_free(&genre->ge_dbe);
|
||||
|
||||
file_readf(&f, "%u", &i);
|
||||
genre = GENRE(genre_ops->dbe_read(&f));
|
||||
file_readu(&f);
|
||||
genre = GENRE(genre_ops->dbe_read(&f, 0));
|
||||
file_close(&f);
|
||||
test_verify_vg(genre);
|
||||
g_free(genre->ge_name);
|
||||
|
@ -66,8 +64,8 @@ static void test_genre_compare()
|
|||
const struct db_ops *genre_ops = test_genre_ops();
|
||||
struct genre *video, *game;
|
||||
|
||||
video = GENRE(genre_ops->dbe_alloc("Video Game Music"));
|
||||
game = GENRE(genre_ops->dbe_alloc("game music"));
|
||||
video = GENRE(genre_ops->dbe_alloc("Video Game Music", 0));
|
||||
game = GENRE(genre_ops->dbe_alloc("game music", 0));
|
||||
|
||||
g_assert_cmpint(genre_compare(video, video), ==, 0);
|
||||
g_assert_cmpint(genre_compare(video, game), ==, 1);
|
||||
|
|
|
@ -20,17 +20,16 @@ static void test_verify_link(struct library *library)
|
|||
|
||||
static void test_library()
|
||||
{
|
||||
struct file f = FILE_INIT_DATA("", "library_tag", 0);
|
||||
const struct db_ops *library_ops = test_library_ops();
|
||||
struct library *link, *zelda, *library;
|
||||
struct file f;
|
||||
|
||||
link = LIBRARY(library_ops->dbe_alloc("/home/Link/Music"));
|
||||
zelda = LIBRARY(library_ops->dbe_alloc("/home/Zelda/Music"));
|
||||
link = LIBRARY(library_ops->dbe_alloc("/home/Link/Music", 0));
|
||||
zelda = LIBRARY(library_ops->dbe_alloc("/home/Zelda/Music", 0));
|
||||
|
||||
test_verify_link(link);
|
||||
test_verify_zelda(zelda);
|
||||
|
||||
file_init(&f, "library_tag", 0);
|
||||
file_open(&f, OPEN_WRITE);
|
||||
library_ops->dbe_write(&f, &link->li_dbe);
|
||||
file_writef(&f, "\n");
|
||||
|
@ -38,14 +37,14 @@ static void test_library()
|
|||
file_close(&f);
|
||||
|
||||
file_open(&f, OPEN_READ);
|
||||
library = LIBRARY(library_ops->dbe_read(&f));
|
||||
library = LIBRARY(library_ops->dbe_read(&f, 0));
|
||||
test_verify_link(library);
|
||||
g_assert_cmpstr_free(library_file(library, "navi.mp3"), ==,
|
||||
"/home/Link/Music/navi.mp3");
|
||||
g_free(library->li_path);
|
||||
library_ops->dbe_free(&library->li_dbe);
|
||||
|
||||
library = LIBRARY(library_ops->dbe_read(&f));
|
||||
library = LIBRARY(library_ops->dbe_read(&f, 0));
|
||||
file_close(&f);
|
||||
test_verify_zelda(library);
|
||||
g_assert_cmpstr_free(library_file(library, "impa.ogg"), ==,
|
||||
|
@ -69,14 +68,15 @@ static void test_library_db()
|
|||
g_assert_cmpuint(library_db_get()->db_size, ==, 0);
|
||||
|
||||
library_db_init();
|
||||
library = library_lookup("/home/Zelda/Music");
|
||||
g_assert_null(library);
|
||||
g_assert_null(library_lookup("/home/Zelda/Music"));
|
||||
library = library_find("/home/Zelda/Music");
|
||||
test_verify_zelda(library);
|
||||
|
||||
g_assert_cmpuint(library_db_get()->db_size, ==, 1);
|
||||
g_assert(library_lookup("/home/Zelda/Music") == library);
|
||||
g_assert(library_lookup("/home/Zelda/Music/Ocarina") == library);
|
||||
g_assert(library_find("/home/Zelda/Music") == library);
|
||||
g_assert(library_find("/home/Zelda/Music/Ocarina") == library);
|
||||
g_assert(library_get(0) == library);
|
||||
g_assert_null(library_get(1));
|
||||
|
||||
|
|
|
@ -12,32 +12,43 @@
|
|||
static struct track *test_alloc(const gchar *key)
|
||||
{
|
||||
const struct db_ops *track_ops = test_track_ops();
|
||||
struct db_entry *dbe = track_ops->dbe_alloc(key);
|
||||
struct db_entry *dbe = track_ops->dbe_alloc(key, 0);
|
||||
g_assert_nonnull(dbe);
|
||||
return TRACK(dbe);
|
||||
}
|
||||
|
||||
static void test_verify_tags(struct track *track)
|
||||
static void test_verify_tags(struct track *track, bool external)
|
||||
{
|
||||
g_assert_cmpstr(track->tr_album->al_name, ==, "Hyrule Symphony");
|
||||
g_assert_cmpuint(track->tr_album->al_year, ==, 1998);
|
||||
g_assert_cmpstr(track->tr_album->al_artist->ar_name, ==, "Koji Kondo");
|
||||
g_assert_cmpstr(track->tr_album->al_genre->ge_name, ==, "Game");
|
||||
g_assert_cmpstr(track->tr_library->li_path, ==, "tests/Music");
|
||||
|
||||
if (external) {
|
||||
g_assert_true(TRACK_IS_EXTERNAL(track));
|
||||
g_assert_null(track->tr_library);
|
||||
} else {
|
||||
g_assert_false(TRACK_IS_EXTERNAL(track));
|
||||
g_assert_cmpstr(track->tr_library->li_path, ==, "tests/Music");
|
||||
}
|
||||
}
|
||||
|
||||
static void test_verify_track(struct track *track)
|
||||
static void test_verify_track(struct track *track, bool external)
|
||||
{
|
||||
const struct db_ops *track_ops = test_track_ops();
|
||||
test_verify_tags(track);
|
||||
|
||||
if (!external)
|
||||
track_free_external(track);
|
||||
test_verify_tags(track, external);
|
||||
|
||||
g_assert_cmpstr(track->tr_title, ==, "Title Theme");
|
||||
g_assert_cmpstr(track->tr_tokens[0], ==, "title");
|
||||
g_assert_cmpstr(track->tr_tokens[1], ==, "theme");
|
||||
g_assert_null(track->tr_tokens[2]);
|
||||
g_assert_null(track->tr_alts[0]);
|
||||
g_assert_cmpstr(track_ops->dbe_key(&track->tr_dbe), ==,
|
||||
"0/Hyrule Symphony/01 - Title Theme.ogg");
|
||||
if (!external)
|
||||
g_assert_cmpstr(track_ops->dbe_key(&track->tr_dbe), ==,
|
||||
"0/Hyrule Symphony/01 - Title Theme.ogg");
|
||||
g_assert_cmpstr_free(track_path(track), ==,
|
||||
"tests/Music/Hyrule Symphony/01 - Title Theme.ogg");
|
||||
g_assert_cmpstr_free(track_last_play(track), ==, "Never");
|
||||
|
@ -50,7 +61,7 @@ static void test_verify_track(struct track *track)
|
|||
static void test_verify_notrack(struct track *track)
|
||||
{
|
||||
const struct db_ops *track_ops = test_track_ops();
|
||||
test_verify_tags(track);
|
||||
test_verify_tags(track, false);
|
||||
|
||||
g_assert_cmpstr(track->tr_title, ==, "");
|
||||
g_assert_null(track->tr_tokens[0]);
|
||||
|
@ -72,17 +83,16 @@ static void test_track()
|
|||
time_t rawtime = time(NULL);
|
||||
struct tm *now = localtime(&rawtime);
|
||||
struct track *track;
|
||||
struct file f;
|
||||
struct file f = FILE_INIT_DATA("", "track_tag", 1);
|
||||
gchar *date;
|
||||
|
||||
file_init(&f, "track_tag", 1);
|
||||
g_assert_nonnull(library_find("tests/Music"));
|
||||
date = string_tm2str(now);
|
||||
|
||||
track = test_alloc("0/Hyrule Symphony/01 - Title Theme.ogg");
|
||||
g_assert_nonnull(track);
|
||||
track->tr_dbe.dbe_index = 0;
|
||||
test_verify_track(track);
|
||||
test_verify_track(track, false);
|
||||
|
||||
g_assert_true( track_match_token(track, "title"));
|
||||
g_assert_true( track_match_token(track, "theme"));
|
||||
|
@ -98,16 +108,16 @@ static void test_track()
|
|||
track_ops->dbe_free(&track->tr_dbe);
|
||||
|
||||
file_open(&f, OPEN_READ);
|
||||
track = TRACK(track_ops->dbe_read(&f));
|
||||
track = TRACK(track_ops->dbe_read(&f, 0));
|
||||
track->tr_dbe.dbe_index = 0;
|
||||
test_verify_notrack(track);
|
||||
|
||||
g_free(track->tr_path);
|
||||
track_ops->dbe_free(&track->tr_dbe);
|
||||
|
||||
track = TRACK(track_ops->dbe_read(&f));
|
||||
track = TRACK(track_ops->dbe_read(&f, 0));
|
||||
track->tr_dbe.dbe_index = 0;
|
||||
test_verify_track(track);
|
||||
test_verify_track(track, false);
|
||||
file_close(&f);
|
||||
|
||||
track_played(track);
|
||||
|
@ -119,6 +129,27 @@ static void test_track()
|
|||
g_free(date);
|
||||
}
|
||||
|
||||
static void test_track_external()
|
||||
{
|
||||
struct track *track = track_alloc_external(
|
||||
"tests/Music/Hyrule Symphony/01 - Title Theme.ogg");
|
||||
|
||||
g_assert_nonnull(track);
|
||||
test_verify_track(track, true);
|
||||
g_assert_cmpuint(track_db_get()->db_size, ==, 0);
|
||||
g_assert_cmpuint(track_db_count_unplayed(), ==, 0);
|
||||
|
||||
track_played(track);
|
||||
g_assert_cmpuint(track_db_count_unplayed(), ==, 0);
|
||||
g_assert_cmpuint(track_db_count_plays(), ==, 0);
|
||||
|
||||
track_free_external(track);
|
||||
g_assert_cmpuint(track_db_count_unplayed(), ==, 0);
|
||||
g_assert_cmpuint(track_db_count_plays(), ==, 0);
|
||||
|
||||
g_assert_null(track_alloc_external("/home/Zelda/Music/ocarina.ogg"));
|
||||
}
|
||||
|
||||
static void test_track_compare()
|
||||
{
|
||||
const struct db_ops *track_ops = test_track_ops();
|
||||
|
@ -209,8 +240,14 @@ static void __test_track_db_subprocess()
|
|||
g_assert_cmpuint(track_db_count_unplayed(), ==, 0);
|
||||
g_assert_cmpuint(track_db_count_plays(), ==, 0);
|
||||
g_assert_cmpuint(track_db_average_plays(), ==, 0);
|
||||
|
||||
g_assert_null(track_lookup(NULL));
|
||||
g_assert_null(track_lookup("/home/Zelda/Music/ocarina.ogg"));
|
||||
g_assert_null(track_lookup(path));
|
||||
|
||||
track = track_add(library, path);
|
||||
test_verify_track(track);
|
||||
test_verify_track(track, false);
|
||||
g_assert(track_lookup(path) == track);
|
||||
g_assert_cmpuint(track_db_count_unplayed(), ==, 1);
|
||||
|
||||
g_assert_null(track_add(library, path));
|
||||
|
@ -226,7 +263,7 @@ static void __test_track_db_subprocess()
|
|||
track_db_commit();
|
||||
db_load(&track_db);
|
||||
g_assert_cmpuint(track_db.db_size, ==, 1);
|
||||
test_verify_track(TRACK(db_first(&track_db)));
|
||||
test_verify_track(TRACK(db_first(&track_db)), false);
|
||||
/* Make sure our unplayed count isn't skewed */
|
||||
db_deinit(&track_db);
|
||||
|
||||
|
@ -302,12 +339,13 @@ int main(int argc, char **argv)
|
|||
int ret;
|
||||
|
||||
setlocale(LC_TIME, "C");
|
||||
idle_init_sync();
|
||||
idle_init(IDLE_SYNC);
|
||||
tags_init();
|
||||
while (idle_run_task()) {}
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/Core/Tags/Track", test_track);
|
||||
g_test_add_func("/Core/Tags/Track/External", test_track_external);
|
||||
g_test_add_func("/Core/Tags/Track/Comparison", test_track_compare);
|
||||
g_test_add_func("/Core/Tags/Track/Database", test_track_db);
|
||||
ret = g_test_run();
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
builder
|
||||
model
|
||||
view
|
||||
queue
|
||||
window
|
||||
idle
|
||||
model
|
||||
filter
|
||||
treeview
|
||||
sidebar
|
||||
playlist
|
||||
artwork
|
||||
audio
|
||||
|
|
|
@ -5,11 +5,14 @@ function(gui_unit_test name)
|
|||
endfunction()
|
||||
|
||||
gui_unit_test(Builder)
|
||||
gui_unit_test(Model)
|
||||
gui_unit_test(View)
|
||||
gui_unit_test(Queue)
|
||||
gui_unit_test(Window)
|
||||
gui_unit_test(Idle)
|
||||
gui_unit_test(Model)
|
||||
gui_unit_test(Filter)
|
||||
gui_unit_test(Treeview)
|
||||
gui_unit_test(Sidebar)
|
||||
|
||||
add_subdirectory(playlists/)
|
||||
gui_unit_test(Playlist)
|
||||
gui_unit_test(Artwork)
|
||||
gui_unit_test(Audio)
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2016 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/core.h>
|
||||
#include <gui/artwork.h>
|
||||
#include <gui/filter.h>
|
||||
#include <gui/model.h>
|
||||
#include <gui/playlist.h>
|
||||
#include <gui/sidebar.h>
|
||||
#include <gui/treeview.h>
|
||||
|
||||
static void test_audio_load(struct track *track)
|
||||
{ gui_artwork_set_cover(); }
|
||||
static void test_change_state(GstState state) { }
|
||||
static void test_config_pause(int n) { }
|
||||
|
||||
static struct audio_callbacks test_audio_cb = {
|
||||
.audio_cb_load = test_audio_load,
|
||||
.audio_cb_state_change = test_change_state,
|
||||
.audio_cb_config_pause = test_config_pause,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ALBUM_ART_TEST
|
||||
enum idle_sync_t test_sync = IDLE_ASYNC;
|
||||
#else /* CONFIG_ALBUM_ART_TEST */
|
||||
enum idle_sync_t test_sync = IDLE_SYNC;
|
||||
#endif /* CONFIG_ALBUM_ART_TEST */
|
||||
|
||||
|
||||
static void test_artwork(void)
|
||||
{
|
||||
const gchar *name;
|
||||
GtkIconSize size;
|
||||
gchar *path;
|
||||
|
||||
g_assert_true(GTK_IS_IMAGE(gui_artwork()));
|
||||
|
||||
gtk_image_get_icon_name(gui_artwork(), &name, &size);
|
||||
g_assert_cmpuint(gtk_image_get_storage_type(gui_artwork()), ==,
|
||||
GTK_IMAGE_ICON_NAME);
|
||||
g_assert_cmpstr(name, ==, "image-missing");
|
||||
g_assert_cmpuint(size, ==, GTK_ICON_SIZE_DIALOG);
|
||||
g_assert_false(gtk_widget_is_sensitive(GTK_WIDGET(gui_artwork())));
|
||||
|
||||
#ifdef CONFIG_ALBUM_ART_TEST
|
||||
audio_load(track_get(0));
|
||||
g_assert_cmpuint(gtk_image_get_storage_type(gui_artwork()), ==,
|
||||
GTK_IMAGE_SURFACE);
|
||||
#endif /* CONFIG_ALBUM_ART_TEST */
|
||||
|
||||
track_get(1)->tr_album = album_find(artist_find("Koji Kondo"),
|
||||
genre_find("Video Game Music"),
|
||||
"Hyrule Symphony 2", 1999);
|
||||
audio_load(track_get(1));
|
||||
gtk_image_get_icon_name(gui_artwork(), &name, &size);
|
||||
g_assert_cmpuint(gtk_image_get_storage_type(gui_artwork()), ==,
|
||||
GTK_IMAGE_ICON_NAME);
|
||||
g_assert_cmpstr(name, ==, "image-missing");
|
||||
g_assert_cmpuint(size, ==, GTK_ICON_SIZE_DIALOG);
|
||||
g_assert_false(gtk_widget_is_sensitive(GTK_WIDGET(gui_artwork())));
|
||||
|
||||
path = album_artwork_path(track_get(0)->tr_album);
|
||||
#ifdef CONFIG_ALBUM_ART_TEST
|
||||
gui_artwork_import(track_get(1), path);
|
||||
g_assert_cmpuint(gtk_image_get_storage_type(gui_artwork()), ==,
|
||||
GTK_IMAGE_SURFACE);
|
||||
#endif /* CONFIG_ALBUM_ART_TEST */
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
gtk_init(&argc, NULL);
|
||||
core_init(&argc, NULL, NULL, &test_audio_cb, test_sync);
|
||||
gui_builder_init("share/ocarina/ocarina.ui");
|
||||
gui_model_init();
|
||||
gui_filter_init();
|
||||
gui_treeview_init();
|
||||
gui_sidebar_init();
|
||||
gui_playlist_init();
|
||||
gui_pl_library_add("tests/Music/Hyrule Symphony");
|
||||
while (idle_run_task()) {}
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/Gui/Artwork", test_artwork);
|
||||
ret = g_test_run();
|
||||
|
||||
core_deinit();
|
||||
gui_treeview_deinit();
|
||||
gui_filter_deinit();
|
||||
gui_model_deinit();
|
||||
gui_builder_deinit();
|
||||
return ret;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue