ocarina/ocarina/playlist.cpp
Bryan Schumaker bf5b8a3bdd libsaria: Remove PlaylistType in favor of flags
Flags let me manually set properties after the playlist has been
created, rather than needing to decide upfront with no way of converting
to something else.

Signed-off-by: Bryan Schumaker <bjschuma@gmail.com>
2012-09-14 20:25:09 -04:00

511 lines
17 KiB
C++

// Copyright (c) 2012 Bryan Schumaker
#include <track.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->get_track(),
2, track->get_title().c_str(),
3, track->get_lenstr().c_str(),
4, track->get_artist().c_str(),
5, track->get_album().c_str(),
6, track->get_year(),
7, track->get_count(),
8, track->get_last_played().c_str(),
9, formatted(track->get_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);
else if (event == PLAYLIST_RM)
playlist_rm(data->plist, data->index);
else if (event == PLAYLIST_UPDATE)
playlist_update(data->plist, data->track, data->index);
else if (event == PLAYLIST_SIZE)
set_playlist_size(data->plist);
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;
libsaria::Playlist *plist = (libsaria::Playlist *)data;
gtk_tree_model_get(model, iter, 0, &track, -1);
return plist->is_visible(track);
}
static void do_filter(GtkWidget *entry, gpointer data)
{
struct PlaylistWidgets *widgets = (struct PlaylistWidgets *)data;
string text = gtk_entry_get_text(GTK_ENTRY(entry));
widgets->playlist->set_filter_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();
string name = playlist->get_name();
if (name == "Library" || name == "Banned") {
ban_selected(treeview, name == "Library");
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_disabled(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());
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->playlist, 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 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 & 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);
}