ocarina/ocarina/playlist.cpp

562 lines
19 KiB
C++

// Copyright (c) 2012 Bryan Schumaker
#include <ban.h>
#include <track.h>
#include <format.h>
#include <library.h>
#include <stdlib.h>
#include "ocarina.h"
static GType types[] = {
G_TYPE_POINTER, // libsaria::Track *
G_TYPE_UINT, // Track number
G_TYPE_STRING, // Title
G_TYPE_STRING, // Length
G_TYPE_STRING, // Artist
G_TYPE_STRING, // Album
G_TYPE_UINT, // Year
G_TYPE_UINT, // Count
G_TYPE_STRING, // Last played
G_TYPE_STRING, // Filepath
};
static string formatted(string str)
{
char *escaped = g_markup_escape_text(str.c_str(), -1);
string ret = escaped;
g_free(escaped);
return ret;
}
static void update_track(libsaria::Track *track, GtkListStore *liststore,
GtkTreeIter *iter)
{
gtk_list_store_set(liststore, iter,
0, track,
1, (*track)[TRACK_TRACK],
2, (*track)[TRACK_TITLE].c_str(),
3, (*track)[TRACK_LENSTR].c_str(),
4, (*track)[TRACK_ARTIST].c_str(),
5, (*track)[TRACK_ALBUM].c_str(),
6, (*track)[TRACK_YEAR],
7, (*track)[TRACK_COUNT],
8, track->get_last_played().c_str(),
9, formatted((*track)[TRACK_FILEPATH]).c_str(),
-1);
}
static void playlist_add(libsaria::Playlist *plist, libsaria::Track *track,
unsigned int index)
{
GtkTreeIter iter;
struct PlaylistWidgets *widgets = find_playlist_widgets(plist);
if (!widgets)
return;
gtk_list_store_insert(widgets->liststore, &iter, index);
update_track(track, widgets->liststore, &iter);
}
static void playlist_rm(libsaria::Playlist *plist, unsigned int index)
{
GtkTreeIter iter;
PlaylistWidgets *widgets = find_playlist_widgets(plist);
gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(widgets->liststore), &iter, NULL, index);
gtk_list_store_remove(widgets->liststore, &iter);
update_length_label(current_playlist());
}
static void playlist_update(libsaria::Playlist *plist, libsaria::Track *track,
unsigned int index)
{
GtkTreeIter iter;
PlaylistWidgets *widgets = find_playlist_widgets(plist);
gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(widgets->liststore), &iter, NULL, index);
update_track(track, widgets->liststore, &iter);
}
static void set_playlist_size(libsaria::Playlist *plist)
{
char buf[11];
struct PlaylistWidgets *widgets = find_playlist_widgets(plist);
if (!widgets)
return;
sprintf(buf, "%u", plist->get_size());
gtk_label_set_text(widgets->size_label, buf);
update_length_label(current_playlist());
}
static void playlist_goto_index(libsaria::Playlist *playlist, unsigned int index)
{
GtkTreeIter iter;
GtkTreePath *path;
struct PlaylistWidgets *widgets = find_playlist_widgets(playlist);
GtkTreeModel *model = GTK_TREE_MODEL(widgets->liststore);
gtk_tree_model_get_iter_first(model, &iter);
path = gtk_tree_model_get_path(model, &iter);
for (unsigned int i = 0; i < index; i++)
gtk_tree_path_next(path);
gtk_tree_view_set_cursor(widgets->treeview, path, NULL, FALSE);
gtk_tree_path_free(path);
}
void update_playlist(notify_t event, libsaria::PlaylistNotification *data)
{
if (event == PLAYLIST_ADD) {
playlist_add(data->plist, data->track, data->index);
set_playlist_size(data->plist);
} else if (event == PLAYLIST_RM) {
playlist_rm(data->plist, data->index);
set_playlist_size(data->plist);
} else if (event == PLAYLIST_UPDATE)
playlist_update(data->plist, data->track, data->index);
else if (event == PLAYLIST_GOTO)
playlist_goto_index(data->plist, data->index);
}
static gboolean is_visible(GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
{
libsaria::Track *track;
struct PlaylistWidgets *widgets = (struct PlaylistWidgets *)data;
if (widgets->filter_text == NULL)
return true;
gtk_tree_model_get(model, iter, 0, &track, -1);
return track->is_visible(widgets->filter_text);
}
static void do_filter(GtkWidget *entry, gpointer data)
{
struct PlaylistWidgets *widgets = (struct PlaylistWidgets *)data;
string text = gtk_entry_get_text(GTK_ENTRY(entry));
widgets->filter_text = libsaria::format_text(text);
gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(widgets->filter));
}
static void track_selected(GtkWidget *treeview, GtkTreePath *path,
GtkTreeViewColumn *col, gpointer data)
{
GtkTreeIter iter;
GtkTreeModel *model;
libsaria::Track *track;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
gtk_tree_model_get_iter(model, &iter, path);
gtk_tree_model_get(model, &iter, 0, &track, -1);
track->load(true);
}
static void move_cursor(GtkTreeView *treeview, const string &key)
{
GtkTreePath *path;
gtk_widget_grab_focus(GTK_WIDGET(treeview));
gtk_tree_view_get_cursor(treeview, &path, NULL);
if (path == NULL)
return;
if (key == "j")
gtk_tree_path_next(path);
else
gtk_tree_path_prev(path);
if (path)
gtk_tree_view_set_cursor(treeview, path, NULL, FALSE);
gtk_tree_path_free(path);
}
static void selected_foreach_list(GtkTreeModel *model, GtkTreePath *path,
GtkTreeIter *iter, gpointer data)
{
libsaria::Track *track;
list<libsaria::Track *> *tracks = (list<libsaria::Track *> *)data;
gtk_tree_model_get(model, iter, 0, &track, -1);
tracks->push_back(track);
}
static void list_selected_tracks(GtkTreeView *treeview, list<libsaria::Track *> *tracks)
{
GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
gtk_tree_selection_selected_foreach(selection, selected_foreach_list, tracks);
}
static void selected_foreach_index(GtkTreeModel *model, GtkTreePath *path,
GtkTreeIter *iter, gpointer data)
{
list<unsigned int> *indices = (list<unsigned int> *)data;
unsigned int *index = (unsigned int *)gtk_tree_path_get_indices(path);
indices->push_back(*index);
}
static void list_selected_indices(GtkTreeView *treeview, list<unsigned int> *indices)
{
GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
gtk_tree_selection_selected_foreach(selection, selected_foreach_index, indices);
}
static void new_playlist(const string &key, GtkTreeView *treeview, bool front)
{
list<libsaria::Track *> tracks;
unsigned int flags = PL_NONE;
list_selected_tracks(treeview, &tracks);
if (tracks.size() == 0)
return;
if (key == "s" || key == "S")
flags |= PL_SORTED | PL_RANDOM;
libsaria::deck::new_playlist(tracks, flags, front);
}
static bool add_to_playlist(GtkTreeView *treeview, int n)
{
list<libsaria::Track *> tracks;
libsaria::Playlist *playlist;
list_selected_tracks(treeview, &tracks);
if (tracks.size() == 0)
return false;
playlist = libsaria::deck::get_playlist(n);
if (playlist)
playlist->push_back(tracks);
return true;
}
static void ban_selected(GtkTreeView *treeview, bool state)
{
list<libsaria::Track *> tracks;
list<libsaria::Track *>::iterator it;
list_selected_tracks(treeview, &tracks);
for (it = tracks.begin(); it != tracks.end(); it++)
(*it)->set_banned(state);
}
static void delete_from_playlist(GtkTreeView *treeview)
{
list<unsigned int> indices;
libsaria::Playlist *playlist = current_playlist();
if (playlist == libsaria::library::get_playlist() || playlist == libsaria::ban::get_banned_plist()) {
ban_selected(treeview, playlist == libsaria::library::get_playlist());
return;
}
list_selected_indices(treeview, &indices);
if (indices.back() == (playlist->get_size() - 1))
move_cursor(treeview, "k");
else
move_cursor(treeview, "j");
playlist->remove_indices(indices);
libsaria::deck::garbage_collect();
}
static bool on_escape_key(GtkTreeView *treeview)
{
GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_count_selected_rows(selection) == 0)
return false;
gtk_tree_selection_unselect_all(selection);
return true;
}
static void on_return_key(GtkTreeView *treeview)
{
GtkTreePath *path;
GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_count_selected_rows(selection) == 0)
return;
gtk_tree_view_get_cursor(treeview, &path, NULL);
gtk_tree_view_row_activated(treeview, path, NULL);
gtk_tree_path_free(path);
}
bool playlist_key_pressed(GtkTreeView *treeview, string &key)
{
if (key == "j" || key == "k")
move_cursor(treeview, key);
else if (key == "s" || key == "q")
new_playlist(key, treeview, false);
else if (key == "S" || key == "Q")
new_playlist(key, treeview, true);
else if (key >= "0" && key <= "9")
return add_to_playlist(treeview, atoi(key.c_str()));
else if (key == "Delete" || key == "d")
delete_from_playlist(treeview);
else if (key == "Return")
on_return_key(treeview);
else if (key == "Escape")
return on_escape_key(treeview);
else
return false;
return true;
}
static void on_menu_item(GtkWidget *item, gpointer data)
{
struct PlaylistWidgets *widgets = current_widgets();
if (item == get_widget("MenuQueueFront"))
new_playlist("Q", widgets->treeview, true);
else if (item == get_widget("MenuQueueBack"))
new_playlist("q", widgets->treeview, false);
else if (item == get_widget("MenuSetFront"))
new_playlist("S", widgets->treeview, true);
else if (item == get_widget("MenuSetBack"))
new_playlist("s", widgets->treeview, false);
else if (item == get_widget("AddPlaylist0"))
add_to_playlist(widgets->treeview, 0);
else if (item == get_widget("AddPlaylist1"))
add_to_playlist(widgets->treeview, 1);
else if (item == get_widget("AddPlaylist2"))
add_to_playlist(widgets->treeview, 2);
else if (item == get_widget("AddPlaylist3"))
add_to_playlist(widgets->treeview, 3);
else if (item == get_widget("AddPlaylist4"))
add_to_playlist(widgets->treeview, 4);
else if (item == get_widget("AddPlaylist5"))
add_to_playlist(widgets->treeview, 5);
else if (item == get_widget("AddPlaylist6"))
add_to_playlist(widgets->treeview, 6);
else if (item == get_widget("AddPlaylist7"))
add_to_playlist(widgets->treeview, 7);
else if (item == get_widget("AddPlaylist8"))
add_to_playlist(widgets->treeview, 8);
else if (item == get_widget("AddPlaylist9"))
add_to_playlist(widgets->treeview, 9);
}
static gboolean entry_keypress(GtkEntry *entry, GdkEvent *event, gpointer data)
{
string key = gdk_keyval_name(event->key.keyval);
if (key == "Return") {
playlist_focus_treeview();
return TRUE;
}
return FALSE;
}
static void show_menu_widget(const string &name, unsigned int plist_num,
unsigned int deck_size)
{
if (plist_num < deck_size)
gtk_widget_show(get_widget(name));
else
gtk_widget_hide(get_widget(name));
}
static void on_click(GtkTreeView *treeview, GdkEvent *event, gpointer data)
{
unsigned int deck_size = libsaria::deck::num_playlists();
if (event->button.button != 3)
return;
show_menu_widget("MenuQueueBack", deck_size, 10);
show_menu_widget("MenuQueueFront", deck_size, 10);
show_menu_widget("MenuSetBack", deck_size, 10);
show_menu_widget("MenuSetFront", deck_size, 10);
show_menu_widget("MenuNewSep", deck_size, 10);
if (deck_size > 0 && deck_size < 10)
gtk_widget_show(get_widget("MenuAddSep"));
else
gtk_widget_hide(get_widget("MenuAddSep"));
show_menu_widget("AddPlaylist0", 0, deck_size);
show_menu_widget("AddPlaylist1", 1, deck_size);
show_menu_widget("AddPlaylist2", 2, deck_size);
show_menu_widget("AddPlaylist3", 3, deck_size);
show_menu_widget("AddPlaylist4", 4, deck_size);
show_menu_widget("AddPlaylist5", 5, deck_size);
show_menu_widget("AddPlaylist6", 6, deck_size);
show_menu_widget("AddPlaylist7", 7, deck_size);
show_menu_widget("AddPlaylist8", 8, deck_size);
show_menu_widget("AddPlaylist9", 9, deck_size);
gtk_menu_popup(GTK_MENU(get_widget("PlaylistMenu")), NULL, NULL, NULL, NULL, event->button.button, event->button.time);
}
static void playlist_close(GtkWidget *button, gpointer data)
{
struct libsaria::Playlist *playlist = (libsaria::Playlist *)data;
libsaria::deck::delete_playlist(playlist);
}
static void playlist_disable(GtkToggleButton *button, gpointer data)
{
struct libsaria::Playlist *playlist = (libsaria::Playlist *)data;
playlist->set_flag(PL_DISABLED, gtk_toggle_button_get_active(button));
}
static void playlist_random(GtkToggleButton *button, gpointer data)
{
struct libsaria::Playlist *playlist = (libsaria::Playlist *)data;
if (playlist == libsaria::library::get_playlist())
libsaria::library::set_random(gtk_toggle_button_get_active(button));
else
playlist->set_flag(PL_RANDOM, gtk_toggle_button_get_active(button));
}
static void playlist_sort(GtkToggleButton *button, gpointer data)
{
struct libsaria::Playlist *playlist = (libsaria::Playlist *)data;
playlist->set_flag(PL_SORTED, gtk_toggle_button_get_active(button));
}
void setup_widgets(struct PlaylistWidgets *widgets, libsaria::Playlist *playlist)
{
unsigned int num_columns = sizeof(types) / sizeof(GType);
GtkTreeSelection *selection;
widgets->playlist = playlist;
widgets->liststore = gtk_list_store_newv(num_columns, types);
widgets->page_box = GTK_BOX(gtk_vbox_new(FALSE, 0));
widgets->treeview = GTK_TREE_VIEW(gtk_tree_view_new());
widgets->filter = gtk_tree_model_filter_new(
GTK_TREE_MODEL(widgets->liststore), NULL);
widgets->size_label = GTK_LABEL(gtk_label_new("0"));
widgets->page_label = GTK_LABEL(gtk_label_new(""));
widgets->entry = GTK_ENTRY(gtk_entry_new());
widgets->filter_text = NULL;
selection = gtk_tree_view_get_selection(widgets->treeview);
gtk_tree_selection_set_mode(selection,GTK_SELECTION_MULTIPLE);
gtk_tree_view_set_model(widgets->treeview, widgets->filter);
gtk_tree_view_set_rules_hint(widgets->treeview, TRUE);
gtk_tree_view_set_enable_search(widgets->treeview, FALSE);
gtk_tree_view_set_tooltip_column(widgets->treeview, 9);
gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(widgets->filter),
is_visible, widgets, NULL);
g_signal_connect(widgets->treeview, "row-activated", G_CALLBACK(track_selected), NULL);
g_signal_connect(widgets->treeview, "button-release-event", G_CALLBACK(on_click), NULL);
g_signal_connect(widgets->entry, "changed", G_CALLBACK(do_filter), widgets);
g_signal_connect(widgets->entry, "key-press-event", G_CALLBACK(entry_keypress), widgets);
}
static GtkWidget *setup_close_button(struct PlaylistWidgets *widgets)
{
GtkWidget *close = gtk_button_new();
GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
gtk_button_set_image(GTK_BUTTON(close), image);
gtk_button_set_relief(GTK_BUTTON(close), GTK_RELIEF_NONE);
gtk_widget_set_name(close, "ocarina-small-button");
g_signal_connect(close, "clicked", G_CALLBACK(playlist_close), widgets->playlist);
return close;
}
static GtkWidget *setup_disable_button(struct PlaylistWidgets *widgets)
{
GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_YES, GTK_ICON_SIZE_MENU);
widgets->disable = GTK_TOGGLE_BUTTON(gtk_toggle_button_new());
gtk_button_set_image(GTK_BUTTON(widgets->disable), image);
gtk_widget_set_name(GTK_WIDGET(widgets->disable), "ocarina-small-button");
g_signal_connect(GTK_WIDGET(widgets->disable), "toggled", G_CALLBACK(playlist_disable), widgets->playlist);
return GTK_WIDGET(widgets->disable);
}
static GtkWidget *setup_random_button(struct PlaylistWidgets *widgets)
{
GtkWidget *image = gtk_image_new_from_file(lib_file("random.png").c_str());
widgets->random = GTK_TOGGLE_BUTTON(gtk_toggle_button_new());
gtk_button_set_image(GTK_BUTTON(widgets->random), image);
gtk_widget_set_name(GTK_WIDGET(widgets->random), "ocarina-small-button");
gtk_toggle_button_set_active(widgets->random, widgets->playlist->check_flag(PL_RANDOM));
g_signal_connect(GTK_WIDGET(widgets->random), "toggled", G_CALLBACK(playlist_random), widgets->playlist);
return GTK_WIDGET(widgets->random);
}
static GtkWidget *setup_sort_button(struct PlaylistWidgets *widgets)
{
GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU);
widgets->sort = GTK_TOGGLE_BUTTON(gtk_toggle_button_new());
gtk_button_set_image(GTK_BUTTON(widgets->sort), image);
gtk_widget_set_name(GTK_WIDGET(widgets->sort), "ocarina-small-button");
gtk_toggle_button_set_active(widgets->sort, widgets->playlist->check_flag(PL_SORTED));
g_signal_connect(GTK_WIDGET(widgets->sort), "toggled", G_CALLBACK(playlist_sort), widgets->playlist);
return GTK_WIDGET(widgets->sort);
}
static void add_column(GtkTreeView *treeview, const string &title, int index,
GtkTreeViewColumnSizing sizing, unsigned int fixed_width)
{
GtkCellRenderer *cell = gtk_cell_renderer_text_new();
GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
title.c_str(), cell, "text", index, NULL);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_column_set_sizing(column, sizing);
gtk_tree_view_column_set_fixed_width(column, fixed_width);
gtk_tree_view_column_set_min_width(column, 1);
gtk_tree_view_column_set_max_width(column, 700);
gtk_tree_view_append_column(treeview, column);
}
void setup_playlist_page(struct PlaylistWidgets *widgets, int flags)
{
GtkWidget *top_box = gtk_hbox_new(FALSE, 0);
GtkWidget *window = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
if (flags & CLOSE_BUTTON)
gtk_box_pack_start(GTK_BOX(top_box), setup_close_button(widgets), FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(top_box), GTK_WIDGET(widgets->entry), TRUE, TRUE, 0);
if (flags & RANDOM_BUTTON)
gtk_box_pack_start(GTK_BOX(top_box), setup_random_button(widgets), FALSE, FALSE, 0);
if (flags & SORT_BUTTON)
gtk_box_pack_start(GTK_BOX(top_box), setup_sort_button(widgets), FALSE, FALSE, 0);
if (flags & DISABLE_BUTTON)
gtk_box_pack_start(GTK_BOX(top_box), setup_disable_button(widgets), FALSE, FALSE, 0);
gtk_box_pack_start(widgets->page_box, top_box, FALSE, FALSE, 0);
gtk_box_pack_start(widgets->page_box, window, TRUE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(widgets->treeview));
add_column(widgets->treeview, "#", 1, GTK_TREE_VIEW_COLUMN_FIXED, 20);
add_column(widgets->treeview, "Title", 2, GTK_TREE_VIEW_COLUMN_FIXED, 300);
add_column(widgets->treeview, "Length", 3, GTK_TREE_VIEW_COLUMN_GROW_ONLY, 1);
add_column(widgets->treeview, "Artist", 4, GTK_TREE_VIEW_COLUMN_FIXED, 125);
add_column(widgets->treeview, "Album", 5, GTK_TREE_VIEW_COLUMN_FIXED, 125);
add_column(widgets->treeview, "Year", 6, GTK_TREE_VIEW_COLUMN_GROW_ONLY, 1);
add_column(widgets->treeview, "Count", 7, GTK_TREE_VIEW_COLUMN_GROW_ONLY, 1);
add_column(widgets->treeview, "Played", 8, GTK_TREE_VIEW_COLUMN_GROW_ONLY, 1);
gtk_widget_show_all(GTK_WIDGET(widgets->page_box));
}
void init_playlist()
{
connect_signal("MenuQueueFront", "activate", G_CALLBACK(on_menu_item), NULL);
connect_signal("MenuSetFront", "activate", G_CALLBACK(on_menu_item), NULL);
connect_signal("MenuQueueBack", "activate", G_CALLBACK(on_menu_item), NULL);
connect_signal("MenuSetBack", "activate", G_CALLBACK(on_menu_item), NULL);
connect_signal("AddPlaylist0", "activate", G_CALLBACK(on_menu_item), NULL);
connect_signal("AddPlaylist1", "activate", G_CALLBACK(on_menu_item), NULL);
connect_signal("AddPlaylist2", "activate", G_CALLBACK(on_menu_item), NULL);
connect_signal("AddPlaylist3", "activate", G_CALLBACK(on_menu_item), NULL);
connect_signal("AddPlaylist4", "activate", G_CALLBACK(on_menu_item), NULL);
connect_signal("AddPlaylist5", "activate", G_CALLBACK(on_menu_item), NULL);
connect_signal("AddPlaylist6", "activate", G_CALLBACK(on_menu_item), NULL);
connect_signal("AddPlaylist7", "activate", G_CALLBACK(on_menu_item), NULL);
connect_signal("AddPlaylist8", "activate", G_CALLBACK(on_menu_item), NULL);
connect_signal("AddPlaylist9", "activate", G_CALLBACK(on_menu_item), NULL);
}