480 lines
10 KiB
C++
480 lines
10 KiB
C++
/*
|
|
* Copyright 2014 (c) Anna Schumaker.
|
|
*/
|
|
extern "C" {
|
|
#include <core/collection.h>
|
|
#include <core/playlist.h>
|
|
#include <core/string.h>
|
|
#include <core/tempq.h>
|
|
#include <gui/builder.h>
|
|
}
|
|
#include <gui/tabs.h>
|
|
|
|
#include <map>
|
|
#include <sstream>
|
|
|
|
|
|
static std::map<queue *, Tab *> queue_mapping;
|
|
|
|
static compare_t sort_fields[] = {
|
|
COMPARE_TRACK, COMPARE_TITLE, COMPARE_LENGTH,
|
|
COMPARE_ARTIST, COMPARE_ALBUM, COMPARE_YEAR,
|
|
COMPARE_GENRE, COMPARE_COUNT, COMPARE_PLAYED
|
|
};
|
|
|
|
static void tempq_added(struct queue *queue, unsigned int pos)
|
|
{
|
|
find_tab(queue)->on_track_added(pos);
|
|
tempq_save(queue, Q_ENABLED);
|
|
}
|
|
|
|
static void tempq_removed(struct queue *queue, unsigned int pos)
|
|
{
|
|
find_tab(queue)->on_track_removed(pos);
|
|
tempq_save(queue, Q_ENABLED);
|
|
}
|
|
|
|
static void tempq_cleared(struct queue *queue, unsigned int n)
|
|
{
|
|
find_tab(queue)->on_tracks_cleared(n);
|
|
}
|
|
|
|
static void tempq_updated(struct queue *queue, unsigned int pos)
|
|
{
|
|
find_tab(queue)->on_track_updated(pos);
|
|
}
|
|
|
|
struct queue_ops tempq_ops = {
|
|
tempq_added,
|
|
tempq_removed,
|
|
tempq_cleared,
|
|
tempq_save,
|
|
tempq_updated,
|
|
};
|
|
|
|
|
|
/**
|
|
*
|
|
* Tab class basics
|
|
*
|
|
*/
|
|
|
|
Tab :: Tab(queue *pq)
|
|
: tab_sorting_count(0), tab_pq(pq), tab_label(NULL)
|
|
{
|
|
queue_mapping[tab_pq] = this;
|
|
|
|
tab_builder = Gtk::Builder::create();
|
|
tab_builder->add_from_file(gui :: share_file("QueueToolbar.ui"));
|
|
tab_builder->add_from_file(gui :: share_file("QueueWindow.ui"));
|
|
tab_builder->get_widget_derived("QueueToolbar", tab_toolbar);
|
|
tab_builder->get_widget_derived("QueueWindow", tab_window);
|
|
|
|
tab_window->init(tab_pq);
|
|
tab_window->q_treeview->signal_key_press_event().connect(sigc::mem_fun(*this,
|
|
&Tab :: on_key_pressed));
|
|
tab_window->q_treeview->signal_button_press_event().connect(sigc::mem_fun(*this,
|
|
&Tab :: on_button_pressed), false);
|
|
|
|
for (unsigned int i = 0; i < tab_window->q_treeview->get_n_columns(); i++)
|
|
tab_window->q_treeview->get_column(i)->signal_clicked().connect(
|
|
sigc::bind<unsigned int> (sigc::mem_fun(
|
|
*this, &Tab::on_column_clicked), i));
|
|
|
|
tab_vbox.set_margin_start(1);
|
|
tab_vbox.set_margin_end(1);
|
|
tab_vbox.set_homogeneous(false);
|
|
|
|
tab_vbox.pack_start(*tab_toolbar, false, true, 2);
|
|
tab_vbox.show();
|
|
|
|
tab_runtime_changed();
|
|
}
|
|
|
|
Tab :: ~Tab() {}
|
|
|
|
|
|
/**
|
|
*
|
|
* QNotifier implementation.
|
|
*
|
|
*/
|
|
|
|
void Tab :: on_track_added(unsigned int row)
|
|
{
|
|
tab_window->q_model->on_row_inserted(row);
|
|
tab_label->set_size();
|
|
tab_runtime_changed();
|
|
}
|
|
|
|
void Tab :: on_track_removed(unsigned int row)
|
|
{
|
|
tab_window->q_model->on_row_deleted(row);
|
|
tab_label->set_size();
|
|
tab_runtime_changed();
|
|
}
|
|
|
|
void Tab :: on_tracks_cleared(unsigned int n)
|
|
{
|
|
if (n > 0) {
|
|
tab_window->q_model->on_cleared(n);
|
|
tab_label->set_size();
|
|
tab_runtime_changed();
|
|
}
|
|
}
|
|
|
|
void Tab :: on_track_updated(unsigned int row)
|
|
{
|
|
tab_window->q_model->on_row_changed(row);
|
|
tab_runtime_changed();
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Tab internal helper functions
|
|
*
|
|
*/
|
|
|
|
int Tab :: tab_page_num()
|
|
{
|
|
Gtk::Notebook *notebook = Glib :: wrap(GTK_NOTEBOOK(gui_builder_widget("o_notebook")), false);
|
|
return notebook->page_num(tab_vbox);
|
|
}
|
|
|
|
bool Tab :: tab_is_cur()
|
|
{
|
|
Gtk::Notebook *notebook = Glib :: wrap(GTK_NOTEBOOK(gui_builder_widget("o_notebook")), false);
|
|
return notebook->page_num(tab_vbox) == notebook->get_current_page();
|
|
}
|
|
|
|
void Tab :: tab_runtime_changed()
|
|
{
|
|
gchar *len = string_sec2str_long(tab_pq->q_length);
|
|
if (tab_is_cur())
|
|
Glib :: wrap(GTK_LABEL(gui_builder_widget("o_queue_time")), false)->set_text(len);
|
|
g_free(len);
|
|
}
|
|
|
|
void Tab :: tab_display_sorting()
|
|
{
|
|
std::string text = "";
|
|
if ((tab_sorting_count > 0) && tab_is_cur())
|
|
text = "Sorting within " + tab_sorting_title;
|
|
Glib :: wrap(GTK_LABEL(gui_builder_widget("o_sorting_indicator")), false)->set_text(text);
|
|
}
|
|
|
|
void Tab :: tab_dec_sort_count()
|
|
{
|
|
tab_sorting_count--;
|
|
tab_display_sorting();
|
|
}
|
|
|
|
void Tab :: tab_unmap()
|
|
{
|
|
queue_mapping.erase(tab_pq);
|
|
}
|
|
|
|
void Tab :: tab_focus_search()
|
|
{
|
|
tab_toolbar->q_search->grab_focus();
|
|
}
|
|
|
|
void Tab :: tab_selected_ids(std::vector<unsigned int> &ids)
|
|
{
|
|
Glib::RefPtr<Gtk::TreeSelection> sel = tab_window->q_treeview->get_selection();
|
|
std::vector<Gtk::TreeModel::Path> rows = sel->get_selected_rows();
|
|
Gtk::TreeModel::Path path;
|
|
|
|
for (unsigned int i = 0; i < rows.size(); i++) {
|
|
path = tab_window->q_filter->convert_path_to_child_path(rows[i]);
|
|
ids.push_back(tab_window->q_model->path_to_id(path));
|
|
}
|
|
sel->unselect_all();
|
|
}
|
|
|
|
void Tab :: tab_queue_add(queue *pq)
|
|
{
|
|
std::vector<unsigned int> ids;
|
|
|
|
tab_selected_ids(ids);
|
|
for (unsigned int i = 0; i < ids.size(); i++)
|
|
queue_add(pq, track_get(ids[i]));
|
|
}
|
|
|
|
bool Tab :: tab_queue_selected(bool random)
|
|
{
|
|
unsigned int flags = random ? Q_RANDOM : 0;
|
|
if (tempq_count() >= 10)
|
|
return true;
|
|
|
|
queue *pq = tempq_alloc(&tempq_ops, flags);
|
|
on_pq_created(pq, tempq_count() - 1);
|
|
tab_queue_add(pq);
|
|
return true;
|
|
}
|
|
|
|
bool Tab :: tab_add_to_queue(unsigned int n)
|
|
{
|
|
if (n >= tempq_count())
|
|
return true;
|
|
|
|
queue *pq = tempq_get(n);
|
|
tab_queue_add(pq);
|
|
return true;
|
|
}
|
|
|
|
bool Tab :: tab_add_to_playlist(enum playlist_t plist)
|
|
{
|
|
std::vector<unsigned int> ids;
|
|
|
|
tab_selected_ids(ids);
|
|
for (unsigned int i = 0; i < ids.size(); i++) {
|
|
if (plist == PL_HIDDEN)
|
|
collection_ban(track_get(ids[i]));
|
|
else
|
|
playlist_add(plist, track_get(ids[i]));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Tab :: tab_favorite_selected()
|
|
{
|
|
return tab_add_to_playlist(PL_FAVORITED);
|
|
}
|
|
|
|
void Tab :: tab_ban_selected()
|
|
{
|
|
tab_add_to_playlist(PL_HIDDEN);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
*
|
|
* Tab callback functions
|
|
*
|
|
*/
|
|
|
|
bool Tab :: on_key_press_event(const std::string &key)
|
|
{
|
|
if (key >= "0" && key <= "9")
|
|
return tab_add_to_queue(atoi(key.c_str()));
|
|
else if (key == "f")
|
|
return tab_favorite_selected();
|
|
else if (key == "q" || key == "r")
|
|
return tab_queue_selected(key == "r");
|
|
else
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void Tab :: on_show_rc_menu()
|
|
{
|
|
std::string item;
|
|
unsigned int size = tempq_count();
|
|
|
|
if (size == 0) {
|
|
Glib :: wrap(GTK_MENU_ITEM(gui_builder_widget("o_add_to_pq")), false)->hide();
|
|
return;
|
|
}
|
|
Glib :: wrap(GTK_MENU_ITEM(gui_builder_widget("o_add_to_pq")), false)->show();
|
|
|
|
if (size == 10)
|
|
Glib :: wrap(GTK_MENU_ITEM(gui_builder_widget("o_new_pq")), false)->hide();
|
|
else
|
|
Glib :: wrap(GTK_MENU_ITEM(gui_builder_widget("o_new_pq")), false)->show();
|
|
|
|
for (unsigned int i = 0; i < 10; i++) {
|
|
item = "o_pq_";
|
|
item += '0' + i;
|
|
if (i < size)
|
|
Glib :: wrap(GTK_MENU_ITEM(gui_builder_widget(item.c_str())), false)->show();
|
|
else
|
|
Glib :: wrap(GTK_MENU_ITEM(gui_builder_widget(item.c_str())), false)->hide();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
*
|
|
* GTK-MM callback functions
|
|
*
|
|
*/
|
|
|
|
bool Tab :: on_key_pressed(GdkEventKey *event)
|
|
{
|
|
std::string key = gdk_keyval_name(event->keyval);
|
|
|
|
if (key.size() >= 3) {
|
|
if (key.substr(0, 3) == "KP_")
|
|
key = key.substr(3);
|
|
}
|
|
|
|
return on_key_press_event(key);
|
|
}
|
|
|
|
void Tab :: on_column_clicked(unsigned int col)
|
|
{
|
|
if (tab_sorting_count == 0) {
|
|
tab_sorting_title = tab_window->q_treeview->get_column(col)->get_title();
|
|
queue_sort(tab_pq, sort_fields[col], true);
|
|
} else
|
|
queue_sort(tab_pq, sort_fields[col], false);
|
|
|
|
tab_sorting_count++;
|
|
tab_display_sorting();
|
|
Glib::signal_timeout().connect_seconds_once(
|
|
sigc::mem_fun(*this, &Tab::tab_dec_sort_count), 2);
|
|
}
|
|
|
|
bool Tab :: on_button_pressed(GdkEventButton *button)
|
|
{
|
|
if (button->button != 3)
|
|
return false;
|
|
|
|
Gtk::TreeModel::Path path;
|
|
if (tab_window->q_treeview->get_path_at_pos(button->x, button->y, path))
|
|
tab_window->q_treeview->get_selection()->select(path);
|
|
|
|
on_show_rc_menu();
|
|
Glib :: wrap(GTK_MENU(gui_builder_widget("o_rc_menu")), false)->popup(button->button, button->time);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
*
|
|
* Global functions
|
|
*
|
|
*/
|
|
|
|
Tab *find_tab(queue *pq)
|
|
{
|
|
std::map<queue *, Tab *>::iterator it;
|
|
it = queue_mapping.find(pq);
|
|
if (it != queue_mapping.end())
|
|
return it->second;
|
|
return NULL;
|
|
}
|
|
|
|
static Tab *find_tab(int num)
|
|
{
|
|
std::map<queue *, Tab *>::iterator it;
|
|
for (it = queue_mapping.begin(); it != queue_mapping.end(); it++) {
|
|
if (it->second->tab_page_num() == num)
|
|
return it->second;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static Tab *cur_tab()
|
|
{
|
|
std::map<queue *, Tab *>::iterator it;
|
|
for (it = queue_mapping.begin(); it != queue_mapping.end(); it++) {
|
|
if (it->second->tab_is_cur())
|
|
return it->second;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void on_switch_page(Gtk::Widget *page, int num)
|
|
{
|
|
Tab *tab = find_tab(num);
|
|
if (tab) {
|
|
tab->tab_runtime_changed();
|
|
tab->tab_display_sorting();
|
|
} else
|
|
Glib :: wrap(GTK_LABEL(gui_builder_widget("o_queue_time")), false)->set_text("");
|
|
}
|
|
|
|
void tab_focus_search()
|
|
{
|
|
int page = Glib :: wrap(GTK_NOTEBOOK(gui_builder_widget("o_notebook")), false)->get_current_page();
|
|
Tab *tab = find_tab(page);
|
|
if (tab)
|
|
tab->tab_focus_search();
|
|
}
|
|
|
|
static void on_new_pq()
|
|
{
|
|
Tab *tab = cur_tab();
|
|
if (tab)
|
|
tab->tab_queue_selected(false);
|
|
}
|
|
|
|
static void on_add_to_favs()
|
|
{
|
|
Tab *tab = cur_tab();
|
|
if (tab)
|
|
tab->tab_favorite_selected();
|
|
}
|
|
|
|
static void on_add_to_banned()
|
|
{
|
|
Tab *tab = cur_tab();
|
|
if (tab)
|
|
tab->tab_ban_selected();
|
|
}
|
|
|
|
static void on_add_to_queue(unsigned int n)
|
|
{
|
|
Tab *tab = cur_tab();
|
|
if (tab)
|
|
tab->tab_add_to_queue(n);
|
|
}
|
|
|
|
static void init_menu_item(const std::string &pq, unsigned int n)
|
|
|
|
{
|
|
Glib :: wrap(GTK_MENU_ITEM(gui_builder_widget(pq.c_str())), false)->signal_activate().connect(
|
|
sigc::bind<unsigned int> (sigc::ptr_fun(on_add_to_queue), n));
|
|
}
|
|
|
|
void init_tabs()
|
|
{
|
|
/* Notebook signals */
|
|
struct Gtk::Notebook *notebook = Glib :: wrap(GTK_NOTEBOOK(gui_builder_widget("o_notebook")), false);
|
|
notebook->signal_switch_page().connect(sigc::ptr_fun(on_switch_page));
|
|
|
|
/* Menu signals */
|
|
Glib :: wrap(GTK_MENU(gui_builder_widget("o_rc_menu")), false)->show_all();
|
|
Glib :: wrap(GTK_MENU_ITEM(gui_builder_widget("o_new_pq")), false)->signal_activate().connect(
|
|
sigc::ptr_fun(on_new_pq));
|
|
Glib :: wrap(GTK_MENU_ITEM(gui_builder_widget("o_add_to_favorites")), false)->signal_activate().connect(
|
|
sigc::ptr_fun(on_add_to_favs));
|
|
Glib :: wrap(GTK_MENU_ITEM(gui_builder_widget("o_add_to_banned")), false)->signal_activate().connect(
|
|
sigc::ptr_fun(on_add_to_banned));
|
|
for (unsigned int i = 0; i < 10; i++) {
|
|
std::string pq = "o_pq_";
|
|
pq += '0' + i;
|
|
init_menu_item(pq, i);
|
|
}
|
|
|
|
/* Initialize other tabs */
|
|
init_history_tab();
|
|
init_collection_tab();
|
|
init_queue_tabs();
|
|
}
|
|
|
|
void post_init_tabs()
|
|
{
|
|
post_init_queue_tabs();
|
|
|
|
unsigned int tab = 0;
|
|
for (tab = 0; tab < tempq_count(); tab++) {
|
|
if (queue_has_flag(tempq_get(tab), Q_ENABLED))
|
|
break;
|
|
}
|
|
Glib :: wrap(GTK_NOTEBOOK(gui_builder_widget("o_notebook")), false)->set_current_page(tab);
|
|
}
|
|
|
|
void cleanup_tabs()
|
|
{
|
|
while (queue_mapping.size() > 0)
|
|
delete queue_mapping.begin()->second;
|
|
}
|