commit 66dd37c0018bd958ea1a644e3552bce66b13f658
parent 5d8e3d19823a3c193b0ad4e8d8fd3ccdf8f77c80
Author: Louis Burda <quent.burda@gmail.com>
Date: Sun, 27 Feb 2022 13:59:28 +0100
Switch from mostly ref based tracking to multi-link architecture
Diffstat:
M | Makefile | | | 2 | +- |
M | src/cmd.c | | | 110 | +++++++++++++++++++++++++++++++++++++------------------------------------------ |
M | src/data.c | | | 628 | ++++++++++++++++++++++++++++++++++++++++++++----------------------------------- |
M | src/data.h | | | 52 | ++++++++++++++++++++++++++++++++++++++-------------- |
M | src/history.c | | | 16 | ++++++++-------- |
M | src/main.c | | | 5 | ++--- |
M | src/player.h | | | 17 | +++++++---------- |
M | src/player_mpd.c | | | 174 | +++++++++++++++++++++++++++++++++++-------------------------------------------- |
M | src/ref.c | | | 33 | +++++++++++++++++---------------- |
M | src/ref.h | | | 3 | +++ |
D | src/tag.c | | | 38 | -------------------------------------- |
D | src/tag.h | | | 14 | -------------- |
D | src/track.c | | | 37 | ------------------------------------- |
D | src/track.h | | | 13 | ------------- |
M | src/tui.c | | | 290 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- |
M | src/util.h | | | 3 | +-- |
16 files changed, 745 insertions(+), 690 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,5 +1,5 @@
CFLAGS = -I src -g $(shell pkg-config --cflags glib-2.0 dbus-1)
-CFLAGS += -I lib/liblist/include
+CFLAGS += -I lib/liblist/include -Wunused-variable
LDLIBS = -lcurses -lmpdclient $(shell pkg-config --libs glib-2.0 dbus-1)
DEPFLAGS = -MT $@ -MMD -MP -MF build/$*.d
diff --git a/src/cmd.c b/src/cmd.c
@@ -4,8 +4,6 @@
#include "list.h"
#include "player.h"
#include "ref.h"
-#include "tag.h"
-#include "track.h"
#include "tui.h"
#include "util.h"
@@ -49,7 +47,7 @@ bool
cmd_move(const char *name)
{
struct link *link;
- struct track *track;
+ struct track *track, *new;
struct tag *tag;
char *newpath;
@@ -58,49 +56,56 @@ cmd_move(const char *name)
link = list_at(tracks_vis, track_nav.sel);
if (!link) CMD_ERROR("No track selected");
- track = UPCAST(link, struct ref)->data;
+ track = tracks_vis_track(link);
newpath = aprintf("%s/%s", tag->fpath, track->name);
OOM_CHECK(newpath);
+ if (!dup_file(track->fpath, newpath)) {
+ free(newpath);
+ CMD_ERROR("Failed to move track");
+ }
+ free(newpath);
- if (!move_file(track->fpath, newpath))
- CMD_ERROR("Failed to move file");
- free(track->fpath);
- track->fpath = newpath;
-
- link_pop(link);
- list_push_back(&tag->tracks, link);
+ new = track_add(tag, track->name);
+ if (!new) {
+ rm_file(track->fpath);
+ ERROR("Failed to move track");
+ }
- playlist_update();
+ if (!track_rm(track, true))
+ ERROR("Failed to move track");
- return 1;
+ return true;
}
bool
cmd_copy(const char *name)
{
struct link *link;
- struct track *track;
- struct ref *ref;
+ struct track *track, *new;
struct tag *tag;
char *newpath;
tag = tag_find(name);
if (!tag) return 0;
- link = list_at(&player.playlist, track_nav.sel);
- if (!link) return 0;
- track = UPCAST(link, struct ref)->data;
+ link = list_at(tracks_vis, track_nav.sel);
+ if (!link) CMD_ERROR("No track selected");
+ track = tracks_vis_track(link);
newpath = aprintf("%s/%s", tag->fpath, track->name);
OOM_CHECK(newpath);
+ if (!dup_file(track->fpath, newpath)) {
+ free(newpath);
+ ERROR("Failed to copy track");
+ }
+ free(newpath);
- copy_file(track->fpath, newpath);
- track->fpath = newpath;
-
- track = track_alloc(tag->fpath, track->name, get_fid(tag->fpath));
- ref = ref_alloc(track);
- list_push_back(&tag->tracks, &ref->link);
+ new = track_add(tag, track->name);
+ if (!new) {
+ rm_file(track->fpath);
+ CMD_ERROR("Failed to copy track");
+ }
return 1;
}
@@ -110,6 +115,7 @@ cmd_reindex(const char *name)
{
struct link *link;
struct tag *tag;
+ struct ref *ref;
struct list matches;
list_init(&matches);
@@ -117,18 +123,21 @@ cmd_reindex(const char *name)
if (!*name) {
link = list_at(&tags, tag_nav.sel);
if (!link) return false;
- tag = UPCAST(link, struct tag);
- list_push_back(&matches, LINK(ref_alloc(tag)));
+ tag = UPCAST(link, struct tag, link);
+ ref = ref_alloc(tag);
+ list_push_back(&matches, &ref->link);
} else if (!strcmp(name, "*")) {
for (LIST_ITER(&tags, link)) {
- tag = UPCAST(link, struct tag);
- list_push_back(&matches, LINK(ref_alloc(tag)));
+ tag = UPCAST(link, struct tag, link);
+ ref = ref_alloc(tag);
+ list_push_back(&matches, &ref->link);
}
} else {
for (LIST_ITER(&tags, link)) {
- tag = UPCAST(link, struct tag);
+ tag = UPCAST(link, struct tag, link);
if (!strcmp(tag->name, name)) {
- list_push_back(&matches, LINK(ref_alloc(tag)));
+ ref = ref_alloc(tag);
+ list_push_back(&matches, &ref->link);
break;
}
}
@@ -137,8 +146,8 @@ cmd_reindex(const char *name)
if (list_empty(&matches)) return false;
for (LIST_ITER(&matches, link)) {
- tag = UPCAST(link, struct ref)->data;
- if (!tracks_update(tag))
+ ref = UPCAST(link, struct ref, link);
+ if (!tracks_update(ref->data))
return false;
}
@@ -152,35 +161,27 @@ cmd_add_tag(const char *name)
{
struct link *link;
struct tag *tag;
- char *fname, *fpath;
+ char *fpath;
for (LIST_ITER(&tags, link)) {
- tag = UPCAST(link, struct tag);
+ tag = UPCAST(link, struct tag, link);
if (!strcmp(tag->name, name))
CMD_ERROR("Tag already exists");
}
- fname = aprintf("%s", name);
- OOM_CHECK(fname);
-
- fpath = aprintf("%s/%s", datadir, fname);
+ fpath = aprintf("%s/%s", datadir, name);
OOM_CHECK(fpath);
if (!make_dir(fpath)) {
- CMD_SET_STATUS("Failed to create dir");
- free(fname);
free(fpath);
- return false;
+ CMD_ERROR("Failed to create dir");
}
- tag = tag_alloc(datadir, fname);
- OOM_CHECK(tag);
-
- list_push_back(&tags, LINK(tag));
-
- free(fname);
free(fpath);
+ tag = tag_add(name);
+ if (!tag) CMD_ERROR("Failed to add tag");
+
return true;
}
@@ -189,15 +190,14 @@ cmd_rm_tag(const char *name)
{
struct link *link;
struct tag *tag;
- char *fname, *fpath;
if (!*name) {
link = list_at(&tags, tag_nav.sel);
if (!link) return false;
- tag = UPCAST(link, struct tag);
+ tag = UPCAST(link, struct tag, link);
} else {
for (LIST_ITER(&tags, link)) {
- tag = UPCAST(link, struct tag);
+ tag = UPCAST(link, struct tag, link);
if (!strcmp(tag->name, name))
break;
}
@@ -206,13 +206,8 @@ cmd_rm_tag(const char *name)
CMD_ERROR("No such tag");
}
- if (!rm_dir(tag->fpath, true)) {
- tracks_update(tag); /* in case some deleted, some not */
- CMD_ERROR("Failed to remove dir");
- }
-
- link_pop(LINK(tag));
- tag_free(tag);
+ if (!tag_rm(tag, true))
+ CMD_ERROR("Failed to remove tag");
return true;
}
@@ -235,7 +230,6 @@ cmd_run(const char *query, bool *found)
{
const char *sep, *args;
int i, cmdlen;
- bool success;
*found = false;
sep = strchr(query, ' ');
diff --git a/src/data.c b/src/data.c
@@ -3,9 +3,6 @@
#include "player.h"
#include "list.h"
#include "log.h"
-#include "ref.h"
-#include "track.h"
-#include "tag.h"
#include "tui.h"
#include <fts.h>
@@ -18,88 +15,21 @@
const char *datadir;
-struct list tracks;
-struct list tags;
-struct list tags_sel;
+struct list tracks; /* struct track (link) */
+struct list tags; /* struct track (link) */
+struct list tags_sel; /* struct tag (link_sel) */
-void
-data_load(void)
-{
- struct dirent *ent;
- struct tag *tag;
- struct stat st;
- char *path;
- DIR *dir;
-
- list_init(&tracks);
- list_init(&tags);
- list_init(&tags_sel);
-
- datadir = getenv("TMUS_DATA");
- if (!datadir) ERROR("TMUS_DATA not set!\n");
-
- dir = opendir(datadir);
- if (!dir) ERROR("Failed to access dir: %s\n", datadir);
-
- while ((ent = readdir(dir))) {
- if (!strcmp(ent->d_name, "."))
- continue;
- if (!strcmp(ent->d_name, ".."))
- continue;
-
- path = aprintf("%s/%s", datadir, ent->d_name);
- OOM_CHECK(path);
-
- if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
- tag = tag_alloc(datadir, ent->d_name);
- OOM_CHECK(tag);
- tracks_load(tag);
- list_push_back(&tags, LINK(tag));
- }
-
- free(path);
- }
- closedir(dir);
-
- list_sort(&tracks, track_fid_compare);
-}
-
-void
-data_save(void)
-{
- struct link *iter;
- struct tag *tag;
-
- for (LIST_ITER(&tags, iter)) {
- tag = UPCAST(iter, struct tag);
- tracks_save(tag);
- }
-}
-
-void
-data_free(void)
-{
- struct link *track_link;
- struct link *tag_link;
- struct tag *tag;
-
- log_info("MAIN: data_free()\n");
+static int get_fid(const char *path);
- refs_free(&tracks);
- refs_free(&tags_sel);
+static struct tag *tag_alloc(const char *path, const char *fname);
+static void tag_free(struct tag *tag);
- while (!list_empty(&tags)) {
- tag_link = list_pop_front(&tags);
+static struct track *track_alloc(const char *path, const char *fname);
+static void track_free(struct track *t);
+static int track_name_compare(struct link *a, struct link *b);
- tag = UPCAST(tag_link, struct tag);
- while (!list_empty(&tag->tracks)) {
- track_link = list_pop_front(&tag->tracks);
- track_free(UPCAST(track_link, struct ref)->data);
- ref_free(UPCAST(track_link, struct ref));
- }
- tag_free(tag);
- }
-}
+static void tracks_load(struct tag *tag);
+static void tracks_save(struct tag *tag);
int
get_fid(const char *path)
@@ -108,168 +38,86 @@ get_fid(const char *path)
return stat(path, &st) ? -1 : st.st_ino;
}
-int
-track_fid_compare(struct link *a, struct link *b)
-{
- struct track *ta, *tb;
-
- ta = UPCAST(a, struct ref)->data;
- tb = UPCAST(b, struct ref)->data;
-
- return ta->fid - tb->fid;
-}
-
-void
-index_update(struct tag *tag)
+struct tag *
+tag_alloc(const char *path, const char *fname)
{
- struct dirent *ent;
- struct stat st;
- char *path;
- FILE *file;
- DIR *dir;
- int fid;
+ struct tag *tag;
- path = aprintf("%s/index", tag->fpath);
- OOM_CHECK(path);
+ tag = malloc(sizeof(struct tag));
+ ASSERT(tag != NULL);
- dir = opendir(tag->fpath);
- if (!dir) ERROR("Failed to access dir: %s\n", tag->fpath);
+ tag->fpath = aprintf("%s/%s", path, fname);
+ ASSERT(tag->fpath != NULL);
- file = fopen(path, "w+");
- if (!file) ERROR("Failed to create file: %s\n", path);
- free(path);
+ tag->name = strdup(fname);
+ ASSERT(tag->name != NULL);
- while ((ent = readdir(dir))) {
- if (!strcmp(ent->d_name, "."))
- continue;
- if (!strcmp(ent->d_name, ".."))
- continue;
- if (!strcmp(ent->d_name, "index"))
- continue;
+ tag->link = LINK_EMPTY;
+ tag->link_sel = LINK_EMPTY;
- /* skip files without extension */
- if (!strchr(ent->d_name + 1, '.'))
- continue;
+ list_init(&tag->tracks);
- path = aprintf("%s/%s", tag->fpath, ent->d_name);
- OOM_CHECK(path);
- fid = get_fid(path);
- free(path);
-
- fprintf(file, "%i:%s\n", fid, ent->d_name);
- }
+ return tag;
+}
- closedir(dir);
- fclose(file);
+void
+tag_free(struct tag *tag)
+{
+ free(tag->fpath);
+ free(tag->name);
+ list_clear(&tag->tracks);
+ free(tag);
}
-bool
-tracks_update(struct tag *tag)
+struct track *
+track_alloc(const char *dir, const char *fname)
{
- struct dirent *ent;
- struct stat st;
- struct link *link;
- struct ref *ref;
struct track *track;
- char *path;
- DIR *dir;
- int fid;
-
- dir = opendir(tag->fpath);
- if (!dir) return false;
- while (!list_empty(&tag->tracks)) {
- link = list_pop_front(&tag->tracks);
- ref = UPCAST(link, struct ref);
- track = ref->data;
- ref_free(ref);
- for (LIST_ITER(&tracks, link)) {
- ref = UPCAST(link, struct ref);
- if (ref->data == track) {
- link = link_pop(link);
- ref_free(ref);
- break;
- }
- }
- if (player.track == track)
- player.track = NULL;
- track_free(track);
- }
+ track = malloc(sizeof(struct track));
+ ASSERT(track != NULL);
- while ((ent = readdir(dir))) {
- if (!strcmp(ent->d_name, "."))
- continue;
- if (!strcmp(ent->d_name, ".."))
- continue;
- if (!strcmp(ent->d_name, "index"))
- continue;
+ track->fpath = aprintf("%s/%s", dir, fname);
+ ASSERT(track->fpath != NULL);
- /* skip files without extension */
- if (!strchr(ent->d_name + 1, '.'))
- continue;
+ track->name = strdup(fname);
+ ASSERT(track->name != NULL);
- path = aprintf("%s/%s", tag->fpath, ent->d_name);
- OOM_CHECK(path);
+ track->tag = NULL;
- fid = get_fid(path);
+ track->link = LINK_EMPTY;
+ track->link_pl = LINK_EMPTY;
+ track->link_tt = LINK_EMPTY;
+ track->link_pq = LINK_EMPTY;
+ track->link_hs = LINK_EMPTY;
- track = track_alloc(tag->fpath, ent->d_name, fid);
- OOM_CHECK(track);
-
- ref = ref_alloc(tag);
- OOM_CHECK(ref);
- list_push_back(&track->tags, LINK(ref));
-
- ref = ref_alloc(track);
- OOM_CHECK(ref);
- list_push_back(&tag->tracks, LINK(ref));
-
- ref = ref_alloc(track);
- OOM_CHECK(ref);
- list_push_back(&tracks, LINK(ref));
-
- free(path);
- }
-
- list_sort(&tracks, track_fid_compare);
-
- playlist_update();
-
- closedir(dir);
- return true;
+ return track;
}
void
-playlist_update(void)
+track_free(struct track *t)
{
- struct link *link, *link2;
- struct track *track;
- struct ref *ref;
- struct tag *tag;
+ free(t->fpath);
+ free(t->name);
+ free(t);
+}
- refs_free(&player.playlist);
- for (LIST_ITER(&tags_sel, link)) {
- tag = UPCAST(link, struct ref)->data;
- for (LIST_ITER(&tag->tracks, link2)) {
- track = UPCAST(link2, struct ref)->data;
- ref = ref_alloc(track);
- ASSERT(ref != NULL);
- list_push_back(&player.playlist, LINK(ref));
- }
- }
+int
+track_name_compare(struct link *a, struct link *b)
+{
+ struct track *ta, *tb;
+
+ ta = UPCAST(a, struct track, link);
+ tb = UPCAST(b, struct track, link);
+
+ return strcmp(ta->name, tb->name);
}
void
tracks_load(struct tag *tag)
{
char linebuf[1024];
- struct link *link;
- struct track *track;
- struct track *track2;
- struct ref *ref;
char *index_path;
- char *track_name, *sep;
- int track_fid;
FILE *file;
index_path = aprintf("%s/index", tag->fpath);
@@ -283,28 +131,10 @@ tracks_load(struct tag *tag)
}
while (fgets(linebuf, sizeof(linebuf), file)) {
- sep = strchr(linebuf, '\n');
- if (sep) *sep = '\0';
-
- sep = strchr(linebuf, ':');
- if (!sep) ERROR("Syntax error in index file: %s\n", index_path);
- *sep = '\0';
-
- track_fid = atoi(linebuf);
- track_name = sep + 1;
- track = track_alloc(tag->fpath, track_name, track_fid);
-
- ref = ref_alloc(tag);
- OOM_CHECK(ref);
- list_push_back(&track->tags, LINK(ref));
-
- ref = ref_alloc(track);
- OOM_CHECK(ref);
- list_push_back(&tag->tracks, LINK(ref));
-
- ref = ref_alloc(track);
- OOM_CHECK(ref);
- list_push_back(&tracks, LINK(ref));
+ if (!*linebuf) continue;
+ if (linebuf[strlen(linebuf) - 1] == '\n')
+ linebuf[strlen(linebuf) - 1] = '\0';
+ track_add(tag, linebuf);
}
fclose(file);
@@ -333,8 +163,8 @@ tracks_save(struct tag *tag)
}
for (LIST_ITER(&tag->tracks, link)) {
- track = UPCAST(link, struct ref)->data;
- fprintf(file, "%i:%s\n", track->fid, track->name);
+ track = UPCAST(link, struct track, link_tt);
+ fprintf(file, "%s\n", track->name);
}
fclose(file);
@@ -342,36 +172,6 @@ tracks_save(struct tag *tag)
}
bool
-track_rm(struct track *track)
-{
- struct link *link;
- struct ref *ref;
- struct tag *tag;
-
- if (!rm_file(track->fpath)) {
- CMD_SET_STATUS("Failed to remove track");
- return false;
- }
-
- refs_rm(&tracks, track);
-
- for (LIST_ITER(&track->tags, link)) {
- ref = UPCAST(link, struct ref);
- tag = ref->data;
- refs_rm(&tag->tracks, track);
- }
-
- refs_rm(&player.playlist, track);
-
- if (player.track == track)
- player.track = NULL;
-
- track_free(track);
-
- return true;
-}
-
-bool
make_dir(const char *path)
{
return mkdir(path, S_IRWXU | S_IRWXG) == 0;
@@ -428,7 +228,7 @@ copy_file(const char *src, const char *dst)
{
FILE *in, *out;
char buf[4096];
- int len, nread;
+ int nread;
bool ok;
ok = false;
@@ -457,25 +257,305 @@ cleanup:
}
bool
+dup_file(const char *src, const char *dst)
+{
+ /* TODO add option for hard link dup if possible */
+ return copy_file(src, dst);
+}
+
+bool
move_file(const char *src, const char *dst)
{
return rename(src, dst) == 0;
}
+void
+index_update(struct tag *tag)
+{
+ struct dirent *ent;
+ char *index_path;
+ FILE *file;
+ DIR *dir;
+
+ dir = opendir(tag->fpath);
+ if (!dir) ERROR("Failed to access dir: %s\n", tag->fpath);
+
+ index_path = aprintf("%s/index", tag->fpath);
+ OOM_CHECK(index_path);
+
+ file = fopen(index_path, "w+");
+ if (!file) ERROR("Failed to create index file in dir %s\n", tag->name);
+ free(index_path);
+
+ while ((ent = readdir(dir))) {
+ if (!strcmp(ent->d_name, "."))
+ continue;
+ if (!strcmp(ent->d_name, ".."))
+ continue;
+
+ /* skip files without extension */
+ if (!strchr(ent->d_name + 1, '.'))
+ continue;
+
+ fprintf(file, "%s\n", ent->d_name);
+ }
+
+ closedir(dir);
+ fclose(file);
+}
+
+bool
+tracks_update(struct tag *tag)
+{
+ struct link *link;
+ struct track *track;
+ struct dirent *ent;
+ DIR *dir;
+
+ dir = opendir(tag->fpath);
+ if (!dir) return false;
+
+ while (!list_empty(&tag->tracks)) {
+ link = list_pop_front(&tag->tracks);
+ track = UPCAST(link, struct track, link_tt);
+ track_rm(track, false);
+ }
+
+ while ((ent = readdir(dir))) {
+ if (!strcmp(ent->d_name, "."))
+ continue;
+ if (!strcmp(ent->d_name, ".."))
+ continue;
+
+ /* skip files without extension */
+ if (!strchr(ent->d_name + 1, '.'))
+ continue;
+
+ track_add(tag, ent->d_name);
+ }
+
+ closedir(dir);
+
+ return true;
+}
+
+struct track *
+tracks_vis_track(struct link *link)
+{
+ if (tracks_vis == &player.playlist) {
+ return UPCAST(link, struct track, link_pl);
+ } else {
+ return UPCAST(link, struct track, link_tt);
+ }
+}
+
+void
+playlist_clear(void)
+{
+ while (!list_empty(&player.playlist))
+ list_pop_front(&player.playlist);
+}
+
+void
+playlist_update(bool exec)
+{
+ static bool update = false;
+ struct link *link, *link2;
+ struct track *track;
+ struct tag *tag;
+
+ if (!exec) {
+ update = true;
+ return;
+ }
+
+ if (!update) return;
+ update = false;
+
+ playlist_clear();
+
+ for (LIST_ITER(&tags_sel, link)) {
+ tag = UPCAST(link, struct tag, link_sel);
+ for (LIST_ITER(&tag->tracks, link2)) {
+ track = UPCAST(link2, struct track, link_tt);
+ link_pop(&track->link_pl);
+ list_push_back(&player.playlist, &track->link_pl);
+ }
+ }
+}
+
struct tag *
-tag_find(const char *query)
+tag_add(const char *fname)
{
- struct link *iter;
struct tag *tag;
- for (LIST_ITER(&tags, iter)) {
- tag = UPCAST(iter, struct tag);
- if (!strcmp(tag->name, query)) {
+ /* add to tags list */
+ tag = tag_alloc(datadir, fname);
+ OOM_CHECK(tag);
+ list_push_back(&tags, &tag->link);
+
+ return tag;
+}
+
+struct tag *
+tag_find(const char *name)
+{
+ struct link *link;
+ struct tag *tag;
+
+ for (LIST_ITER(&tags, link)) {
+ tag = UPCAST(link, struct tag, link);
+ if (!strcmp(tag->name, name))
return tag;
- }
}
return NULL;
}
+bool
+tag_rm(struct tag *tag, bool sync_fs)
+{
+ struct link *link;
+ struct track *track;
+
+ /* remove contained tracks */
+ while (!list_empty(&tag->tracks)) {
+ link = list_pop_front(&tag->tracks);
+ track = UPCAST(link, struct track, link_tt);
+ if (!track_rm(track, sync_fs))
+ return false;
+ }
+
+ /* delete dir and remaining non-track files */
+ if (sync_fs && !rm_dir(tag->fpath, true))
+ return false;
+
+ /* remove from selected */
+ link_pop(&tag->link_sel);
+
+ /* remove from tags list */
+ link_pop(&tag->link);
+
+ tag_free(tag);
+
+ return true;
+}
+
+struct track *
+track_add(struct tag *tag, const char *fname)
+{
+ struct track *track;
+
+ track = track_alloc(tag->fpath, fname);
+ OOM_CHECK(track);
+
+ track->tag = tag;
+
+ /* insert track into sorted tracks list */
+ list_push_back(&tracks, &track->link);
+
+ /* add to tag's tracks list */
+ list_push_back(&tag->tracks, &track->link_tt);
+
+ /* if track's tag is selected, update playlist */
+ if (link_inuse(&tag->link_sel))
+ playlist_update(false);
+
+ return track;
+}
+
+bool
+track_rm(struct track *track, bool sync_fs)
+{
+ if (sync_fs && !rm_file(track->fpath))
+ return false;
+
+ /* remove from tracks list */
+ link_pop(&track->link);
+
+ /* remove from tag's track list */
+ link_pop(&track->link_tt);
+
+ /* remove from playlist */
+ link_pop(&track->link_pl);
+
+ /* remove from player queue */
+ link_pop(&track->link_pq);
+
+ /* remove the reference as last used track */
+ if (player.track == track)
+ player.track = NULL;
+
+ track_free(track);
+
+ return true;
+}
+
+void
+data_load(void)
+{
+ struct dirent *ent;
+ struct tag *tag;
+ struct stat st;
+ char *path;
+ DIR *dir;
+
+ list_init(&tracks);
+ list_init(&tags);
+ list_init(&tags_sel);
+
+ datadir = getenv("TMUS_DATA");
+ if (!datadir) ERROR("TMUS_DATA not set!\n");
+
+ dir = opendir(datadir);
+ if (!dir) ERROR("Failed to access dir: %s\n", datadir);
+
+ while ((ent = readdir(dir))) {
+ if (!strcmp(ent->d_name, "."))
+ continue;
+ if (!strcmp(ent->d_name, ".."))
+ continue;
+
+ path = aprintf("%s/%s", datadir, ent->d_name);
+ OOM_CHECK(path);
+
+ if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
+ tag = tag_add(ent->d_name);
+ tracks_load(tag);
+ }
+
+ free(path);
+ }
+
+ closedir(dir);
+}
+
+void
+data_save(void)
+{
+ struct link *link;
+ struct tag *tag;
+
+ for (LIST_ITER(&tags, link)) {
+ tag = UPCAST(link, struct tag, link);
+ tracks_save(tag);
+ }
+}
+
+void
+data_free(void)
+{
+ struct link *link;
+ struct tag *tag;
+
+ list_clear(&player.playlist);
+ list_clear(&player.queue);
+ list_clear(&player.history);
+
+ while (!list_empty(&tags)) {
+ link = list_pop_front(&tags);
+ tag = UPCAST(link, struct tag, link);
+ tag_rm(tag, false);
+ }
+}
diff --git a/src/data.h b/src/data.h
@@ -1,33 +1,57 @@
#pragma once
-#include "tag.h"
-#include "track.h"
+#include "list.h"
-void data_load(void);
-void data_save(void);
-void data_free(void);
+#include <stdbool.h>
-int get_fid(const char *path);
-int track_fid_compare(struct link *a, struct link *b);
-void index_update(struct tag *tag);
-bool tracks_update(struct tag *tag);
-void playlist_update(void);
+struct tag {
+ char *name, *fpath;
+ struct list tracks;
-void tracks_load(struct tag *tag);
-void tracks_save(struct tag *tag);
+ struct link link; /* tags list */
+ struct link link_sel; /* selected tags list */
+};
-bool track_rm(struct track *track);
+struct track {
+ char *name, *fpath;
+ struct tag *tag;
+
+ struct link link; /* tracks list */
+ struct link link_pl; /* player playlist */
+ struct link link_tt; /* tag tracks list */
+ struct link link_pq; /* player queue */
+ struct link link_hs; /* player history */
+};
bool make_dir(const char *path);
bool rm_dir(const char *path, bool recursive);
bool rm_file(const char *path);
bool copy_file(const char *dst, const char *src);
+bool dup_file(const char *dst, const char *src);
bool move_file(const char *dst, const char *src);
-struct tag *tag_find(const char *query);
+void index_update(struct tag *tag);
+bool tracks_update(struct tag *tag);
+
+struct track *tracks_vis_track(struct link *link);
+
+void playlist_clear(void);
+void playlist_update(bool exec);
+
+struct tag *tag_add(const char *fname);
+struct tag *tag_find(const char *name);
+bool tag_rm(struct tag *tag, bool sync_fs);
+
+struct track *track_add(struct tag *tag, const char *fname);
+bool track_rm(struct track *track, bool sync_fs);
+
+void data_load(void);
+void data_save(void);
+void data_free(void);
extern const char *datadir;
extern struct list tracks; /* struct ref */
extern struct list tags; /* struct tag */
extern struct list tags_sel; /* struct ref */
+
diff --git a/src/history.c b/src/history.c
@@ -18,7 +18,7 @@ history_list_prev(struct inputln *cur, const char *search)
struct inputln *ln;
for (iter = cur->link.prev; iter && iter->prev; iter = iter->prev) {
- ln = UPCAST(iter, struct inputln);
+ ln = UPCAST(iter, struct inputln, link);
if (!search || !*search || strcasestr(ln->buf, search))
return ln;
}
@@ -34,7 +34,7 @@ history_list_next(struct inputln *cur, const char *search)
iter = cur->link.next;
while (LIST_INNER(iter)) {
- ln = UPCAST(iter, struct inputln);
+ ln = UPCAST(iter, struct inputln, link);
if (!search || !*search || strcasestr(ln->buf, search))
return ln;
iter = iter->next;
@@ -57,8 +57,8 @@ history_deinit(struct history *history)
struct link *link;
struct inputln *ln;
- link = link_pop(LINK(history->input));
- ln = UPCAST(link, struct inputln);
+ link = link_pop(&history->input->link);
+ ln = UPCAST(link, struct inputln, link);
inputln_free(ln);
history->input = NULL;
@@ -73,12 +73,12 @@ history_submit(struct history *history)
{
/* if chose from history free input */
if (history->sel != history->input) {
- link_pop(LINK(history->input));
+ link_pop(&history->input->link);
inputln_free(history->input);
}
/* pop first in case already in history */
- link_pop(LINK(history->sel));
+ link_pop(&history->sel->link);
history_add(history, history->sel);
/* create new input buf and add to hist */
@@ -108,11 +108,11 @@ history_add(struct history *history, struct inputln *line)
if (list_len(&history->list) == HISTORY_MAX) {
/* pop last item to make space */
back = list_pop_back(&history->list);
- ln = UPCAST(back, struct inputln);
+ ln = UPCAST(back, struct inputln, link);
inputln_free(ln);
}
- list_push_front(&history->list, LINK(line));
+ list_push_front(&history->list, &line->link);
}
void
diff --git a/src/main.c b/src/main.c
@@ -8,7 +8,6 @@
#include "player.h"
#include "ref.h"
#include "style.h"
-#include "tag.h"
#include "tui.h"
#include "util.h"
@@ -42,11 +41,11 @@ cleanup(int exitcode, void* arg)
{
tui_restore();
+ player_deinit();
+
data_save();
data_free();
- player_deinit();
-
dbus_deinit();
tui_deinit();
diff --git a/src/player.h b/src/player.h
@@ -1,6 +1,5 @@
#pragma once
-#include "track.h"
#include "list.h"
#include "util.h"
@@ -29,21 +28,19 @@ enum {
};
struct player {
+ /* list of tracks to choose from on prev / next */
+ struct list playlist; /* struct track (link_pl) */
+
/* played track history */
- struct list history; /* struct ref -> struct track */
- struct link *history_sel; /* position in history */
+ struct list history; /* struct track (link_hs) */
/* queued tracks */
- struct list queue; /* struct ref -> struct track */
+ struct list queue; /* struct track (link_pq) */
- /* selected track, not (yet) part of history or queue */
+ /* last used track */
struct track *track;
- /* list of tracks to choose from on prev / next */
- struct list playlist; /* struct ref -> struct track */
- struct link *playlist_sel; /* position in playlist */
-
- /* a track is loaded, not necessarily player.track */
+ /* player has a track is loaded (not necessarily player.track) */
bool loaded;
/* automatically select new tracks when queue empty */
diff --git a/src/player_mpd.c b/src/player_mpd.c
@@ -1,9 +1,9 @@
#include "player.h"
-#include "ref.h"
-#include "portaudio.h"
-#include "sndfile.h"
+#include "data.h"
+#include "list.h"
#include "util.h"
+#include "log.h"
#include <mpd/client.h>
#include <mpd/song.h>
@@ -81,15 +81,15 @@ mpd_handle_status(int status)
}
bool
-history_contains(struct track *track, int depth)
+history_contains(struct track *cmp, int depth)
{
struct link *link;
- struct ref *ref;
+ struct track *track;
link = list_back(&player.history);
while (LIST_INNER(link) && depth-- > 0) {
- ref = UPCAST(link, struct ref);
- if (track == ref->data)
+ track = UPCAST(link, struct track, link_hs);
+ if (track == cmp)
return true;
link = link->prev;
}
@@ -102,7 +102,6 @@ playlist_track_lru(int skip)
{
struct track *track;
struct link *link;
- struct ref *ref;
int len;
track = NULL;
@@ -110,8 +109,7 @@ playlist_track_lru(int skip)
len = list_len(&player.playlist);
link = list_front(&player.playlist);
while (skip >= 0 && LIST_INNER(link)) {
- ref = UPCAST(link, struct ref);
- track = ref->data;
+ track = UPCAST(link, struct track, link_pl);
if (!history_contains(track, len - 1))
skip -= 1;
@@ -123,102 +121,101 @@ playlist_track_lru(int skip)
link = list_front(&player.playlist);
}
- PLAYER_STATUS(INFO, "%i, %i", len - 1, skip);
-
return track;
}
+struct track *
+player_next_from_playlist(void)
+{
+ struct link *link;
+ int index;
+
+ if (list_empty(&player.playlist))
+ return NULL;
+
+ if (player.track)
+ player_add_history(player.track);
+
+ if (player.shuffle) {
+ index = rand() % list_len(&player.playlist);
+ return playlist_track_lru(index);
+ } else {
+ if (player.track && link_inuse(&player.track->link_pl)) {
+ link = player.track->link_pl.next;
+ if (!link) list_front(&player.playlist);
+ } else {
+ link = list_front(&player.playlist);
+ }
+ return UPCAST(link, struct track, link_pl);
+ }
+
+ return NULL;
+}
+
void
player_play_prev(void)
{
- struct link *link, *next;
+ struct link *next;
struct track *track;
- struct ref *ref;
if (list_empty(&player.history))
return;
- if (!player.history_sel) {
+ if (!player.track || !link_inuse(&player.track->link_hs)) {
next = list_back(&player.history);
- } else if (LIST_INNER(player.history_sel->prev)) {
- next = player.history_sel->prev;
+ } else if (LIST_INNER(player.track->link_hs.prev)) {
+ next = player.track->link_hs.prev;
} else {
return;
}
- ref = UPCAST(next, struct ref);
- player_play_track(ref->data);
-
- player.history_sel = next;
+ track = UPCAST(next, struct track, link_hs);
+ player_play_track(track);
}
void
player_play_next(void)
{
+ struct track *next_track;
struct link *link;
- struct track *track, *next_track;
- struct ref *ref;
- int index, len;
- int status;
next_track = NULL;
- link = player.history_sel;
- if (link && LIST_INNER(link->next)) {
- player.history_sel = link->next;
- ref = UPCAST(link->next, struct ref);
- next_track = ref->data;
+ if (player.track && link_inuse(&player.track->link_hs)
+ && LIST_INNER(player.track->link_hs.next)) {
+ next_track = UPCAST(player.track->link_hs.next,
+ struct track, link_hs);
+ } else if (!list_empty(&player.queue)) {
+ link = list_pop_front(&player.queue);
+ next_track = UPCAST(link, struct track, link_pq);
} else {
- if (!list_empty(&player.queue)) {
- link = list_pop_front(&player.queue);
- ref = UPCAST(link, struct ref);
- next_track = ref->data;
- ref_free(ref);
- } else {
- if (list_empty(&player.playlist))
- return;
-
- if (!player.history_sel) {
- player_add_history(player.track);
- player.track = NULL;
- }
- player.history_sel = NULL;
-
- if (player.shuffle) {
- index = rand() % list_len(&player.playlist);
- next_track = playlist_track_lru(index + 1);
- ASSERT(next_track != NULL);
- } else {
- link = player.playlist_sel;
- if (link && LIST_INNER(link->next)) {
- ref = UPCAST(link->next, struct ref);
- next_track = ref->data;
- } else {
- status = mpd_run_clear(mpd.conn);
- mpd_handle_status(status);
- return;
- }
- }
- }
+ next_track = player_next_from_playlist();
}
+ if (!next_track) goto clear;
+
player_play_track(next_track);
+ return;
+
+clear:
+ player.track = NULL;
+ mpd_run_clear(mpd.conn);
}
void
-player_add_history(struct track *track)
+player_add_history(struct track *new)
{
struct link *link;
- struct ref *ref;
+ struct track *track;
link = list_back(&player.history);
if (link) {
- ref = UPCAST(link, struct ref);
- if (ref->data == track) return;
+ track = UPCAST(link, struct track, link_hs);
+ if (track == new) return;
}
- ref = ref_alloc(track);
- list_push_back(&player.history, LINK(ref));
+ link_pop(&new->link_hs);
+ list_push_back(&player.history, &new->link_hs);
}
void
@@ -228,19 +225,22 @@ player_init(void)
mpd.action = PLAYER_ACTION_NONE;
mpd.seek_delay = 0;
+ list_init(&player.playlist);
list_init(&player.history);
- player.history_sel = NULL;
list_init(&player.queue);
+
player.track = NULL;
- list_init(&player.playlist);
- player.playlist_sel = NULL;
+
player.loaded = 0;
player.autoplay = true;
player.shuffle = true;
+
player.state = PLAYER_STATE_PAUSED;
player.volume = 0;
+
player.time_pos = 0;
player.time_end = 0;
+
player.status = NULL;
player.status_lvl = PLAYER_STATUS_MSG_INFO;
}
@@ -248,13 +248,9 @@ player_init(void)
void
player_deinit(void)
{
- struct link *iter;
-
- if (!mpd.conn) return;
-
- refs_free(&player.queue);
- refs_free(&player.history);
- refs_free(&player.playlist);
+ list_clear(&player.playlist);
+ list_clear(&player.queue);
+ list_clear(&player.history);
if (player.status) free(player.status);
if (mpd.conn) mpd_connection_free(mpd.conn);
@@ -265,7 +261,6 @@ player_update(void)
{
struct mpd_status *status;
struct mpd_song *current_song;
- struct ref *ref;
bool queue_empty;
if (!mpd.conn) {
@@ -283,10 +278,8 @@ player_update(void)
current_song = mpd_run_current_song(mpd.conn);
if (!current_song) {
- if (player.track && !player.history_sel) {
+ if (player.track)
player_add_history(player.track);
- player.track = NULL;
- }
queue_empty = list_empty(&player.queue);
if (player.loaded && player.autoplay || !queue_empty)
@@ -359,8 +352,6 @@ player_update(void)
int
player_play_track(struct track *track)
{
- struct link *link;
- struct ref *ref;
int status;
status = mpd_run_clear(mpd.conn);
@@ -374,22 +365,9 @@ player_play_track(struct track *track)
if (mpd_handle_status(status))
return PLAYER_STATUS_ERR;
- if (player.track && !player.history_sel) {
+ /* add last track to history */
+ if (player.track && !link_inuse(&player.track->link_hs))
player_add_history(player.track);
- player.track = NULL;
- }
-
- player.history_sel = NULL;
- /* re-assigning history_sel done in calling code */
-
- player.playlist_sel = NULL;
- for (LIST_ITER(&player.playlist, link)) {
- ref = UPCAST(link, struct ref);
- if (ref->data == track) {
- player.playlist_sel = link;
- break;
- }
- }
player.track = track;
diff --git a/src/ref.c b/src/ref.c
@@ -27,37 +27,37 @@ refs_free(struct list *list)
list_free(list, ref_free, LINK_OFFSET(struct ref, link));
}
-static struct link *
-refs_ffind(struct list *list, void *data)
+int
+refs_index(struct list *list, void *data)
{
struct link *iter;
struct ref *ref;
+ int index;
+ index = 0;
for (LIST_ITER(list, iter)) {
- ref = UPCAST(iter, struct ref);
+ ref = UPCAST(iter, struct ref, link);
if (ref->data == data)
- return iter;
+ return index;
+ index++;
}
- return NULL;
+ return -1;
}
-int
-refs_index(struct list *list, void *data)
+struct link *
+refs_find(struct list *list, void *data)
{
struct link *iter;
struct ref *ref;
- int index;
- index = 0;
for (LIST_ITER(list, iter)) {
- ref = UPCAST(iter, struct ref);
+ ref = UPCAST(iter, struct ref, link);
if (ref->data == data)
- return index;
- index++;
+ return iter;
}
- return -1;
+ return NULL;
}
int
@@ -65,7 +65,8 @@ refs_incl(struct list *list, void *data)
{
struct link *ref;
- ref = refs_ffind(list, data);
+ ref = refs_find(list, data);
+
return ref != NULL;
}
@@ -75,10 +76,10 @@ refs_rm(struct list *list, void *data)
struct link *ref;
struct ref *dataref;
- ref = refs_ffind(list, data);
+ ref = refs_find(list, data);
if (!ref) return;
- dataref = UPCAST(ref, struct ref);
+ dataref = UPCAST(ref, struct ref, link);
link_pop(ref);
free(dataref);
}
diff --git a/src/ref.h b/src/ref.h
@@ -13,6 +13,9 @@ void ref_free(void *ref);
void refs_free(struct list *list);
int refs_index(struct list *list, void *data);
+
+struct link *refs_find(struct list *list, void *data);
int refs_incl(struct list *list, void *data);
+
void refs_rm(struct list *list, void *data);
diff --git a/src/tag.c b/src/tag.c
@@ -1,38 +0,0 @@
-#include "tag.h"
-
-#include "link.h"
-#include "ref.h"
-#include "util.h"
-
-#include <string.h>
-
-struct tag *
-tag_alloc(const char *path, const char *fname)
-{
- struct tag *tag;
- int len;
-
- tag = malloc(sizeof(struct tag));
- ASSERT(tag != NULL);
-
- tag->fpath = aprintf("%s/%s", path, fname);
- ASSERT(tag->fpath != NULL);
-
- tag->name = strdup(fname);
- ASSERT(tag->name != NULL);
-
- tag->link = LINK_EMPTY;
-
- list_init(&tag->tracks);
-
- return tag;
-}
-
-void
-tag_free(struct tag *tag)
-{
- free(tag->fpath);
- free(tag->name);
- refs_free(&tag->tracks);
- free(tag);
-}
diff --git a/src/tag.h b/src/tag.h
@@ -1,14 +0,0 @@
-#pragma once
-
-#include "list.h"
-
-struct tag {
- char *name, *fpath;
-
- struct list tracks;
-
- struct link link;
-};
-
-struct tag *tag_alloc(const char *path, const char *fname);
-void tag_free(struct tag *tag);
diff --git a/src/track.c b/src/track.c
@@ -1,37 +0,0 @@
-#include "ref.h"
-#include "track.h"
-
-#include <wchar.h>
-#include <string.h>
-#include <sys/stat.h>
-
-struct track *
-track_alloc(const char *dir, const char *fname, int fid)
-{
- struct track *track;
- int len;
-
- track = malloc(sizeof(struct track));
- ASSERT(track != NULL);
-
- track->fpath = aprintf("%s/%s", dir, fname);
- ASSERT(track->fpath != NULL);
-
- track->name = strdup(fname);
- ASSERT(track->name != NULL);
-
- track->fid = fid;
-
- list_init(&track->tags);
-
- return track;
-}
-
-void
-track_free(struct track *t)
-{
- free(t->fpath);
- free(t->name);
- refs_free(&t->tags);
- free(t);
-}
diff --git a/src/track.h b/src/track.h
@@ -1,13 +0,0 @@
-#pragma once
-
-#include "list.h"
-#include "util.h"
-
-struct track {
- char *name, *fpath;
- struct list tags;
- int fid;
-};
-
-struct track *track_alloc(const char *dir, const char *file, int fid);
-void track_free(struct track *t);
diff --git a/src/tui.c b/src/tui.c
@@ -10,7 +10,6 @@
#include "player.h"
#include "list.h"
#include "listnav.h"
-#include "ref.h"
#include "style.h"
#include "strbuf.h"
#include "util.h"
@@ -45,10 +44,15 @@ static char *track_name_gen(const char *text, int fwd, int state);
static char *tag_name_gen(const char *text, int fwd, int state);
static void toggle_current_tag(void);
+static void select_only_current_tag(void);
+static void seek_next_selected_tag(void);
+static void delete_selected_tag(void);
static bool tag_pane_input(wint_t c);
static void tag_pane_vis(struct pane *pane, int sel);
+static void delete_selected_track(void);
+
static bool track_pane_input(wint_t c);
static void track_pane_vis(struct pane *pane, int sel);
@@ -61,7 +65,7 @@ static bool cmd_pane_input(wint_t c);
static void cmd_pane_vis(struct pane *pane, int sel);
static void queue_hover(void);
-static void update_track_playlist(void);
+static void update_tracks_vis(void);
static void main_input(wint_t c);
static void main_vis(void);
@@ -159,26 +163,26 @@ char *
track_name_gen(const char *text, int fwd, int reset)
{
static struct link *cur;
- struct link *iter;
+ struct link *link;
struct track *track;
char *dup;
if (reset) {
cur = tracks.head.next;
- iter = cur;
+ link = cur;
} else {
- iter = fwd ? cur->next : cur->prev;
+ link = fwd ? cur->next : cur->prev;
}
- while (LIST_INNER(iter)) {
- track = UPCAST(iter, struct ref)->data;
+ while (LIST_INNER(link)) {
+ track = UPCAST(link, struct track, link);
if (strcasestr(track->name, text)) {
- cur = iter;
+ cur = link;
dup = strdup(track->name);
OOM_CHECK(dup);
return dup;
}
- iter = fwd ? iter->next : iter->prev;
+ link = fwd ? link->next : link->prev;
}
return NULL;
@@ -188,26 +192,26 @@ char *
tag_name_gen(const char *text, int fwd, int reset)
{
static struct link *cur;
- struct link *iter;
+ struct link *link;
struct tag *tag;
char *dup;
if (reset) {
cur = tags.head.next;
- iter = cur;
+ link = cur;
} else {
- iter = fwd ? cur->next : cur->prev;
+ link = fwd ? cur->next : cur->prev;
}
- while (LIST_INNER(iter)) {
- tag = UPCAST(iter, struct tag);
+ while (LIST_INNER(link)) {
+ tag = UPCAST(link, struct tag, link);
if (strcasestr(tag->name, text)) {
- cur = iter;
+ cur = link;
dup = strdup(tag->name);
OOM_CHECK(dup);
return dup;
}
- iter = fwd ? iter->next : iter->prev;
+ link = fwd ? link->next : link->prev;
}
return NULL;
@@ -216,59 +220,111 @@ tag_name_gen(const char *text, int fwd, int reset)
void
toggle_current_tag(void)
{
- struct link *link, *iter;
- struct track *track;
+ struct link *link;
struct tag *tag;
- struct ref *ref;
- int in_tags, in_playlist;
if (list_empty(&tags)) return;
link = list_at(&tags, tag_nav.sel);
ASSERT(link != NULL);
- tag = UPCAST(link, struct tag);
+ tag = UPCAST(link, struct tag, link);
/* toggle tag in tags_sel */
- if (refs_incl(&tags_sel, tag)) {
- refs_rm(&tags_sel, tag);
+ if (link_inuse(&tag->link_sel)) {
+ link_pop(&tag->link_sel);
} else {
- ref = ref_alloc(tag);
- list_push_back(&tags_sel, LINK(ref));
+ list_push_back(&tags_sel, &tag->link_sel);
}
+}
- /* rebuild the full playlist */
- playlist_update();
+void
+select_only_current_tag(void)
+{
+ list_clear(&tags_sel);
+
+ toggle_current_tag();
+}
+
+void
+seek_next_selected_tag(void)
+{
+ struct link *link;
+ struct tag *tag;
+ int index;
+
+ if (list_empty(&tags_sel))
+ return;
+
+ if (list_empty(&tags))
+ return;
+
+ link = list_at(&tags, track_nav.sel);
+ if (!link) return;
+
+ index = track_nav.sel;
+ tag = UPCAST(link, struct tag, link);
+ do {
+ index += 1;
+ link = tag->link.next;
+ if (!LIST_INNER(link)) {
+ link = list_at(&tags, 0);
+ index = 0;
+ }
+ tag = UPCAST(link, struct tag, link);
+ } while (!link_inuse(&tag->link_sel));
+
+ listnav_update_sel(&track_nav, index);
+}
+
+void
+delete_selected_tag(void)
+{
+ struct link *link;
+ struct tag *tag;
+
+ link = list_at(&tags, track_nav.sel);
+ if (!link) return;
+ tag = UPCAST(link, struct tag, link);
+ tag_rm(tag, true);
}
bool
tag_pane_input(wint_t c)
{
switch (c) {
- case KEY_UP:
+ case KEY_UP: /* nav up */
listnav_update_sel(&tag_nav, tag_nav.sel - 1);
return true;
- case KEY_DOWN:
+ case KEY_DOWN: /* nav down */
listnav_update_sel(&tag_nav, tag_nav.sel + 1);
return true;
- case KEY_SPACE:
+ case KEY_SPACE: /* toggle tag */
toggle_current_tag();
+ playlist_update(false);
return true;
- case KEY_ENTER:
- refs_free(&tags_sel);
- toggle_current_tag();
+ case KEY_ENTER: /* select only current tag */
+ select_only_current_tag();
+ playlist_update(false);
return true;
- case KEY_PPAGE:
+ case KEY_PPAGE: /* seek half a page up */
listnav_update_sel(&tag_nav, tag_nav.sel - tag_nav.wlen / 2);
return true;
- case KEY_NPAGE:
+ case KEY_NPAGE: /* seek half a page down */
listnav_update_sel(&tag_nav, tag_nav.sel + tag_nav.wlen / 2);
return true;
- case 'g':
+ case 'g': /* seek start of list */
listnav_update_sel(&tag_nav, 0);
- break;
- case 'G':
+ return true;
+ case 'G': /* seek end of list */
listnav_update_sel(&tag_nav, tag_nav.max - 1);
- break;
+ return true;
+ case 'n': /* nav through selected tags */
+ seek_next_selected_tag();
+ return true;
+ case 'D': /* delete tag */
+ delete_selected_tag();
+ playlist_update(false);
+ return true;
}
return false;
@@ -278,7 +334,7 @@ void
tag_pane_vis(struct pane *pane, int sel)
{
struct tag *tag;
- struct link *iter;
+ struct link *link;
int index, tagsel;
werase(pane->win);
@@ -288,9 +344,9 @@ tag_pane_vis(struct pane *pane, int sel)
listnav_update_wlen(&tag_nav, pane->h - 1);
index = -1;
- for (LIST_ITER(&tags, iter)) {
- tag = UPCAST(iter, struct tag);
- tagsel = refs_incl(&tags_sel, tag);
+ for (LIST_ITER(&tags, link)) {
+ tag = UPCAST(link, struct tag, link);
+ tagsel = link_inuse(&tag->link_sel);
index += 1;
if (index < tag_nav.wmin) continue;
@@ -318,56 +374,80 @@ tag_pane_vis(struct pane *pane, int sel)
}
}
-bool
-track_pane_input(wint_t c)
+void
+play_selected_track(void)
+{
+ struct link *link;
+ struct track *track;
+
+ link = list_at(tracks_vis, track_nav.sel);
+ if (!link) return;
+ track = tracks_vis_track(link);
+ player_play_track(track);
+}
+
+void
+seek_playing_track(void)
{
struct link *link;
struct track *track;
int index;
+ index = 0;
+ for (LIST_ITER(tracks_vis, link)) {
+ track = tracks_vis_track(link);
+ if (track == player.track) {
+ listnav_update_sel(&track_nav, index);
+ break;
+ }
+ index += 1;
+ }
+}
+
+void
+delete_selected_track(void)
+{
+ struct link *link;
+ struct track *track;
+
+ link = list_at(tracks_vis, track_nav.sel);
+ ASSERT(link != NULL);
+ track = tracks_vis_track(link);
+ track_rm(track, true);
+}
+
+bool
+track_pane_input(wint_t c)
+{
switch (c) {
- case KEY_UP:
+ case KEY_UP: /* nav up */
listnav_update_sel(&track_nav, track_nav.sel - 1);
return true;
- case KEY_DOWN:
+ case KEY_DOWN: /* nav down */
listnav_update_sel(&track_nav, track_nav.sel + 1);
return true;
- case KEY_ENTER:
- link = list_at(tracks_vis, track_nav.sel);
- if (!link) return true;
- track = UPCAST(link, struct ref)->data;
- player_play_track(track);
+ case KEY_ENTER: /* play track */
+ play_selected_track();
return true;
- case KEY_PPAGE:
+ case KEY_PPAGE: /* seek half page up */
listnav_update_sel(&track_nav,
track_nav.sel - track_nav.wlen / 2);
return true;
- case KEY_NPAGE:
+ case KEY_NPAGE: /* seek half page down */
listnav_update_sel(&track_nav,
track_nav.sel + track_nav.wlen / 2);
return true;
- case 'g':
+ case 'g': /* seek start of list */
listnav_update_sel(&track_nav, 0);
break;
- case 'G':
+ case 'G': /* seek end of list */
listnav_update_sel(&track_nav, track_nav.max - 1);
break;
- case 'n':
- index = 0;
- for (LIST_ITER(tracks_vis, link)) {
- track = UPCAST(link, struct ref)->data;
- if (track == player.track) {
- listnav_update_sel(&track_nav, index);
- break;
- }
- index += 1;
- }
+ case 'n': /* seek playing */
+ seek_playing_track();
break;
- case 'D':
- link = list_at(tracks_vis, track_nav.sel);
- ASSERT(link != NULL);
- track = UPCAST(link, struct ref)->data;
- track_rm(track);
+ case 'D': /* delete track */
+ delete_selected_track();
break;
}
@@ -378,8 +458,7 @@ void
track_pane_vis(struct pane *pane, int sel)
{
struct track *track;
- struct link *iter;
- struct tag *tag;
+ struct link *link;
int index;
werase(pane->win);
@@ -389,8 +468,8 @@ track_pane_vis(struct pane *pane, int sel)
listnav_update_wlen(&track_nav, pane->h - 1);
index = -1;
- for (LIST_ITER(tracks_vis, iter)) {
- track = UPCAST(iter, struct ref)->data;
+ for (LIST_ITER(tracks_vis, link)) {
+ track = tracks_vis_track(link);
index += 1;
if (index < track_nav.wmin) continue;
@@ -436,20 +515,21 @@ bool
play_track(const char *query)
{
struct track *track;
- struct link *iter;
- int fid;
+ struct link *link;
+ const char *prevname;
- fid = -1;
- for (LIST_ITER(&tracks, iter)) {
- track = UPCAST(iter, struct ref)->data;
- if (fid == track->fid) continue;
+ prevname = NULL;
+ for (LIST_ITER(&tracks, link)) {
+ track = UPCAST(link, struct track, link);
+ if (prevname && !strcmp(track->name, prevname))
+ continue;
if (!strcmp(track->name, query)) {
player_play_track(track);
return true;
}
- fid = track->fid;
+ prevname = track->name;
}
return false;
@@ -464,10 +544,11 @@ select_track(const char *query)
index = 0;
for (LIST_ITER(tracks_vis, link)) {
- track = UPCAST(link, struct ref)->data;
+ track = tracks_vis_track(link);
if (!strcmp(track->name, query)) {
listnav_update_sel(&track_nav, index);
pane_after_cmd = track_pane;
+ playlist_update(false);
return true;
}
index += 1;
@@ -480,13 +561,13 @@ bool
select_tag(const char *query)
{
struct tag *tag;
- struct link *iter;
+ struct link *link;
int index;
index = -1;
- for (LIST_ITER(&tags, iter)) {
+ for (LIST_ITER(&tags, link)) {
index += 1;
- tag = UPCAST(iter, struct tag);
+ tag = UPCAST(link, struct tag, link);
if (!strcmp(tag->name, query)) {
listnav_update_sel(&tag_nav, index);
pane_after_cmd = tag_pane;
@@ -594,7 +675,7 @@ cmd_pane_vis(struct pane *pane, int sel)
{
static struct strbuf line = { 0 };
struct inputln *cmd;
- struct link *iter;
+ struct link *link;
int index, offset;
werase(pane->win);
@@ -654,12 +735,12 @@ cmd_pane_vis(struct pane *pane, int sel)
cmd = history->sel;
if (cmd != history->input) {
index = 0;
- for (LIST_ITER(&history->list, iter)) {
- if (UPCAST(iter, struct inputln) == cmd)
+ for (LIST_ITER(&history->list, link)) {
+ if (UPCAST(link, struct inputln, link) == cmd)
break;
index += 1;
}
- strbuf_append(&line, "[%i] ", iter ? index : -1);
+ strbuf_append(&line, "[%i] ", link ? index : -1);
} else {
strbuf_append(&line, "%c", imode_prefix[cmd_input_mode]);
}
@@ -687,17 +768,17 @@ void
queue_hover(void)
{
struct link *link;
- struct ref *ref;
+ struct track *track;
- link = list_at(&player.playlist, track_nav.sel);
+ link = list_at(tracks_vis, track_nav.sel);
if (!link) return;
- ref = UPCAST(link, struct ref);
- list_push_back(&player.queue, ref->data);
+ track = tracks_vis_track(link);
+ list_push_back(&player.queue, &track->link_pq);
}
void
-update_track_playlist(void)
+update_tracks_vis(void)
{
struct link *link;
struct tag *tag;
@@ -707,7 +788,7 @@ update_track_playlist(void)
} else {
link = list_at(&tags, tag_nav.sel);
if (!link) return;
- tag = UPCAST(link, struct tag);
+ tag = UPCAST(link, struct tag, link);
tracks_vis = &tag->tracks;
}
}
@@ -739,10 +820,10 @@ main_input(wint_t c)
queue_hover();
break;
case L'o':
- refs_free(&player.queue);
+ list_clear(&player.queue);
break;
case L'h':
- refs_free(&player.history);
+ list_clear(&player.history);
break;
case L'c':
player_toggle_pause();
@@ -874,9 +955,9 @@ tui_curses_init(void)
void
tui_resize(void)
{
- struct link *iter;
+ struct link *link;
struct tag *tag;
- int i, leftw;
+ int leftw;
getmaxyx(stdscr, scrh, scrw);
@@ -890,8 +971,8 @@ tui_resize(void)
/* adjust tag pane width to name lengths */
leftw = 0;
- for (LIST_ITER(&tags, iter)) {
- tag = UPCAST(iter, struct tag);
+ for (LIST_ITER(&tags, link)) {
+ tag = UPCAST(link, struct tag, link);
leftw = MAX(leftw, strlen(tag->name));
}
leftw = MAX(leftw + 1, 0.2f * scrw);
@@ -933,7 +1014,7 @@ tui_init(void)
listnav_init(&track_nav);
track_show_playlist = 0;
- update_track_playlist();
+ update_tracks_vis();
tui_resize();
}
@@ -977,7 +1058,8 @@ tui_update(void)
if (!handled) main_input(c);
}
- update_track_playlist();
+ playlist_update(true);
+ update_tracks_vis();
refresh();
for (i = 0; i < ARRLEN(panes); i++) {
diff --git a/src/util.h b/src/util.h
@@ -11,8 +11,7 @@
#define OOM_CHECK(x) assert((x) != NULL, __FILE__, __LINE__, "Out of Memory!")
#define ERROR(...) error("" __VA_ARGS__)
-#define LINK(p) (&(p)->link)
-#define UPCAST(iter, type) LINK_UPCAST(iter, type, link)
+#define UPCAST(iter, type, link) LINK_UPCAST(iter, type, link)
void panic(const char *file, int line, const char *msg, ...);
void assert(int cond, const char *file, int line, const char *condstr);