/* * Copyright 2014 (c) Anna Schumaker. */ #include #include #include #include #include #include static std::map queue_mapping; static sort_t sort_fields[] = { SORT_TRACK, SORT_TITLE, SORT_LENGTH, SORT_ARTIST, SORT_ALBUM, SORT_YEAR, SORT_GENRE, SORT_COUNT, SORT_PLAYED }; /** * * Tab class basics * */ Tab :: Tab(Queue *pq) : tab_sorting_count(0), tab_pq(pq), tab_label(NULL) { pq->set_notifier(this); 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 (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_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 = gui :: get_widget("o_notebook"); return notebook->page_num(tab_vbox); } bool Tab :: tab_is_cur() { Gtk::Notebook *notebook = gui :: get_widget("o_notebook"); return notebook->page_num(tab_vbox) == notebook->get_current_page(); } void Tab :: tab_runtime_changed() { if (tab_is_cur()) gui :: get_widget("o_queue_time")->set_text(string :: sec2str_detailed(tab_pq->length())); } void Tab :: tab_display_sorting() { std::string text = ""; if ((tab_sorting_count > 0) && tab_is_cur()) text = "Sorting within " + tab_sorting_title; gui :: get_widget("o_sorting_indicator")->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 &ids) { Glib::RefPtr sel = tab_window->q_treeview->get_selection(); std::vector 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 ids; tab_selected_ids(ids); for (unsigned int i = 0; i < ids.size(); i++) pq->add(tags :: get_track(ids[i])); } bool Tab :: tab_queue_selected(bool random) { if (deck :: get_queues().size() >= 10) return true; Queue *pq = deck :: create(random); on_pq_created(pq, deck :: get_queues().size() - 1); tab_queue_add(pq); return true; } bool Tab :: tab_add_to_queue(unsigned int n) { if (n >= deck :: get_queues().size()) return true; Queue *pq = deck :: get(n); tab_queue_add(pq); return true; } bool Tab :: tab_add_to_playlist(const std::string &playlist) { std::vector ids; tab_selected_ids(ids); for (unsigned int i = 0; i < ids.size(); i++) playlist :: add(tags :: get_track(ids[i]), playlist); return true; } bool Tab :: tab_favorite_selected() { return tab_add_to_playlist("Favorites"); } void Tab :: tab_ban_selected() { tab_add_to_playlist("Banned"); } /** * * 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 = deck :: get_queues().size(); if (size == 0) { gui :: get_widget("o_add_to_pq")->hide(); return; } gui :: get_widget("o_add_to_pq")->show(); if (size == 10) gui :: get_widget("o_new_pq")->hide(); else gui :: get_widget("o_new_pq")->show(); for (unsigned int i = 0; i < 10; i++) { item = "o_pq_"; item += '0' + i; if (i < size) gui :: get_widget(item)->show(); else gui :: get_widget(item)->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(); tab_pq->sort(sort_fields[col], true); } else tab_pq->sort(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(); gui :: get_widget("o_rc_menu")->popup(button->button, button->time); return true; } /** * * Global functions * */ Tab *find_tab(Queue *pq) { std::map::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::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::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 gui :: get_widget("o_queue_time")->set_text(""); } void tab_focus_search() { int page = gui :: get_widget("o_notebook")->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) { gui :: get_widget(pq)->signal_activate().connect( sigc::bind (sigc::ptr_fun(on_add_to_queue), n)); } void init_tabs() { /* Notebook signals */ struct Gtk::Notebook *notebook = gui :: get_widget("o_notebook"); notebook->signal_switch_page().connect(sigc::ptr_fun(on_switch_page)); /* Menu signals */ gui :: get_widget("o_rc_menu")->show_all(); gui :: get_widget("o_new_pq")->signal_activate().connect( sigc::ptr_fun(on_new_pq)); gui :: get_widget("o_add_to_favorites")->signal_activate().connect( sigc::ptr_fun(on_add_to_favs)); gui :: get_widget("o_add_to_banned")->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 < deck::get_queues().size(); tab++) { if ((deck :: get(tab)->has_flag(Q_ENABLED))) break; } gui :: get_widget("o_notebook")->set_current_page(tab); } void cleanup_tabs() { while (queue_mapping.size() > 0) delete queue_mapping.begin()->second; }