2008-10-21 13:36:22 UTC
previous
next
/* Echo Media Player
* Copyright (C) 2008 Shane O'Connell
*
* [ The original file includes a copyright header in this location describing
* the file as being released under the terms of the GNU General Public
* License. It has been removed in order to display the file as part of the
* archive. ]
*/
#include "MainWindow.h"
#include "TrackSlider.h"
#include "../db/Database.h"
#include <iostream>
using namespace std;
MainWindow::MainWindow()
{
set_title("Echo Media Player");
set_default_size(800, 600);
init_actiongroups();
init_main_vbox();
m_refStatusIcon = Gtk::StatusIcon::create(Gtk::Stock::HOME);
m_refStatusIcon->signal_activate().connect(
sigc::mem_fun(*this, &MainWindow::on_status_icon_click));
m_refStatusIcon->signal_popup_menu().connect(
sigc::mem_fun(*this, &MainWindow::on_status_icon_popup));
m_sources_pane.signal_library_opened().connect(
sigc::mem_fun(m_tab_pane, &TabPane::open_library));
m_sources_pane.signal_now_playing_opened().connect(
sigc::mem_fun(m_tab_pane, &TabPane::open_now_playing));
notify_init("Echo Media Player");
m_notify = NULL;
PlaybackManager::signal_track_started().connect(
sigc::mem_fun(*this, &MainWindow::show_libnotify_popup));
PlaybackManager::signal_track_started().connect(
sigc::mem_fun(*this, &MainWindow::on_media_started));
PlaybackManager::signal_track_stopped().connect(
sigc::mem_fun(*this, &MainWindow::on_media_stopped));
show_all_children();
m_progress_pane.hide();
}
MainWindow::~MainWindow()
{
notify_uninit();
}
void MainWindow::init_actiongroups()
{
m_refActionGroup = Gtk::ActionGroup::create();
m_refActionGroup->add(Gtk::Action::create("FileMenu", "_File"));
m_refActionGroup->add(Gtk::Action::create("FileNewPlaylist", "New _Playlist"),
sigc::mem_fun(m_tab_pane, &TabPane::add_blank_playlist));
m_refActionGroup->add(Gtk::Action::create("FileNewSmartPlaylist", "New _Smart Playlist"));
m_refActionGroup->add(Gtk::Action::create("FileOpen", Gtk::Stock::OPEN));
m_refActionGroup->add(Gtk::Action::create("FileOpenURL", "Open _URL"));
m_refActionGroup->add(
Gtk::Action::create("FileRefreshLibraries", Gtk::Stock::REFRESH, "_Refresh Libraries"),
sigc::mem_fun(*this, &MainWindow::on_menu_file_refresh));
m_refActionGroup->add(Gtk::Action::create("FileClose", Gtk::Stock::CLOSE),
sigc::mem_fun(*this, &MainWindow::on_menu_file_close));
m_refActionGroup->add(Gtk::Action::create("FileQuit", Gtk::Stock::QUIT),
sigc::mem_fun(*this, &MainWindow::on_menu_file_quit));
m_refActionGroup->add(Gtk::Action::create("ViewMenu", "_View"));
m_refActionGroup->add(Gtk::ToggleAction::create("ViewBrowser", "_Browser Pane"),
sigc::mem_fun(*this, &MainWindow::on_view_library_pane));
m_refActionGroup->add(Gtk::Action::create("ViewFullscreen", "_Fullscreen"));
m_refActionGroup->add(Gtk::Action::create("PlaybackMenu", "_Playback"));
m_refActionGroup->add(Gtk::Action::create("PlayPause", Gtk::Stock::MEDIA_PLAY),
sigc::mem_fun(*this, &MainWindow::on_play));
m_refActionGroup->add(Gtk::Action::create("PreviousTrack", Gtk::Stock::MEDIA_PREVIOUS),
sigc::ptr_fun(&PlaybackManager::prev));
m_refActionGroup->add(Gtk::Action::create("NextTrack", Gtk::Stock::MEDIA_NEXT),
sigc::ptr_fun(&PlaybackManager::next));
m_refActionGroup->add(Gtk::ToggleAction::create("PlaybackStopWhenFinished", "Stop When _Finished"));
Gtk::RadioButtonGroup repeatGroup;
m_refActionGroup->add(Gtk::Action::create("RepeatMenu", "_Repeat"));
m_refActionGroup->add(Gtk::RadioAction::create(repeatGroup, "RepeatOff", "Repeat _Off"));
m_refActionGroup->add(Gtk::RadioAction::create(repeatGroup, "RepeatAll", "Repeat _All"));
m_refActionGroup->add(Gtk::RadioAction::create(repeatGroup, "RepeatSingle", "Repeat _Single"));
Gtk::RadioButtonGroup shuffleGroup;
m_refActionGroup->add(Gtk::Action::create("ShuffleMenu", "_Shuffle"));
m_refActionGroup->add(
Gtk::RadioAction::create(shuffleGroup, "ShuffleOff", "Shuffle _Off"),
sigc::bind(sigc::mem_fun(*this, &MainWindow::set_shuffle), SHUFFLE_NONE));
m_refActionGroup->add(
Gtk::RadioAction::create(shuffleGroup, "ShuffleBySong", "Shuffle _All"),
sigc::bind(sigc::mem_fun(*this, &MainWindow::set_shuffle), SHUFFLE_ALL));
m_refActionGroup->add(
Gtk::RadioAction::create(shuffleGroup, "ShuffleByArtist", "Shuffle By _Artist"),
sigc::bind(sigc::mem_fun(*this, &MainWindow::set_shuffle), SHUFFLE_ARTIST));
m_refActionGroup->add(
Gtk::RadioAction::create(shuffleGroup, "ShuffleByAlbum", "Shuffle By Al_bum"),
sigc::bind(sigc::mem_fun(*this, &MainWindow::set_shuffle), SHUFFLE_ALBUM));
m_refActionGroup->add(Gtk::Action::create("HelpMenu", "_Help"));
m_refActionGroup->add(Gtk::Action::create("HelpAbout", Gtk::Stock::ABOUT));
m_refActionGroup->add(Gtk::Action::create("NextTrackMenu", "_Shufflee"));
m_refUIManager = Gtk::UIManager::create();
m_refUIManager->insert_action_group(m_refActionGroup);
add_accel_group(m_refUIManager->get_accel_group());
Glib::ustring ui_info =
"<ui>"
" <menubar name='MenuBar'>"
" <menu action='FileMenu'>"
" <menuitem action='FileNewPlaylist'/>"
" <menuitem action='FileNewSmartPlaylist'/>"
" <separator/>"
" <menuitem action='FileOpen'/>"
" <menuitem action='FileOpenURL'/>"
" <separator/>"
" <menuitem action='FileRefreshLibraries'/>"
" <separator/>"
" <menuitem action='FileClose'/>"
" <menuitem action='FileQuit'/>"
" </menu>"
" <menu action='ViewMenu'>"
" <menuitem action='ViewBrowser'/>"
" <menuitem action='ViewFullscreen'/>"
" </menu>"
" <menu action='PlaybackMenu'>"
" <menuitem action='PlayPause'/>"
" <menuitem action='NextTrack'/>"
" <menuitem action='PreviousTrack'/>"
" <separator/>"
" <menuitem action='PlaybackStopWhenFinished'/>"
" <menu action='RepeatMenu'>"
" <menuitem action='RepeatOff'/>"
" <separator/>"
" <menuitem action='RepeatAll'/>"
" <menuitem action='RepeatSingle'/>"
" </menu>"
" <menu action='ShuffleMenu'>"
" <menuitem action='ShuffleOff'/>"
" <separator/>"
" <menuitem action='ShuffleBySong'/>"
" <menuitem action='ShuffleByArtist'/>"
" <menuitem action='ShuffleByAlbum'/>"
" </menu>"
" </menu>"
" <menu action='HelpMenu'>"
" <menuitem action='HelpAbout'/>"
" </menu>"
" </menubar>"
" <toolbar name='Toolbar'>"
" <toolitem action='PreviousTrack'/>"
" <toolitem action='PlayPause'/>"
" </toolbar>"
" <popup name='StatusIconPopup'>"
" <menuitem action='PlayPause'/>"
" <menuitem action='PreviousTrack'/>"
" <menuitem action='NextTrack'/>"
" <separator/>"
" <menuitem action='FileQuit'/>"
" </popup>"
" <popup name='NextTrackPopup'>"
" <menuitem action='ShuffleOff'/>"
" <separator/>"
" <menuitem action='ShuffleBySong'/>"
" <menuitem action='ShuffleByArtist'/>"
" <menuitem action='ShuffleByAlbum'/>"
" </popup>"
"</ui>";
m_refUIManager->add_ui_from_string(ui_info);
}
void MainWindow::init_main_vbox()
{
Gtk::Box* pMainVBox = Gtk::manage(new Gtk::VBox());
add(*pMainVBox);
// Add menu
Gtk::Widget* pMenuBar = m_refUIManager->get_widget("/MenuBar");
pMainVBox->pack_start(*pMenuBar, Gtk::PACK_SHRINK);
// Add toolbar
Gtk::Toolbar* pToolbar = static_cast<Gtk::Toolbar*>(m_refUIManager->get_widget("/Toolbar"));
pToolbar->set_toolbar_style(Gtk::TOOLBAR_ICONS);
Gtk::MenuToolButton* pNextTrackButton = Gtk::manage(new Gtk::MenuToolButton());
m_refActionGroup->get_action("NextTrack")->connect_proxy(*pNextTrackButton);
//Gtk::MenuItem* pShuffleMenu = static_cast<Gtk::MenuItem*>(
// m_refUIManager->get_widget("/MenuBar/ShuffleMenu"));
Gtk::Menu* pShuffleMenu = static_cast<Gtk::Menu*>(
m_refUIManager->get_widget("/NextTrackPopup"));
pNextTrackButton->set_menu(*pShuffleMenu);
pToolbar->append(*pNextTrackButton);
pToolbar->append(*Gtk::manage(new Gtk::SeparatorToolItem()));
// TODO when track titles are too long this doesn't work
Gtk::ToolItem* pTrackInfoItem = Gtk::manage(new Gtk::ToolItem());
TrackInfoPane* pInfoPane = new TrackInfoPane(); // TODO destroy this somewhere
pTrackInfoItem->add(*pInfoPane);
pTrackInfoItem->set_expand(true);
pToolbar->append(*pTrackInfoItem);
pToolbar->append(*Gtk::manage(new Gtk::SeparatorToolItem()));
Gtk::ToolItem* pScaleToolItem = Gtk::manage(new Gtk::ToolItem());
TrackSlider* track_slider = Gtk::manage(new TrackSlider()); // TODO is this properly destroyed?
pScaleToolItem->add(*track_slider);
pScaleToolItem->set_expand(true);
pToolbar->append(*pScaleToolItem);
pToolbar->append(*Gtk::manage(new Gtk::SeparatorToolItem()));
//Gtk::ToolItem* pVolumeToolItem = Gtk::manage(new Gtk::ToolItem());
//Gtk::VolumeButton* pVolumeButton = Gtk::manage(new Gtk::VolumeButton());
Gtk::ToolButton* pVolumeButton = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::EXECUTE));
pToolbar->append(*pVolumeButton);
pMainVBox->pack_start(*pToolbar, Gtk::PACK_SHRINK);
// Add main HPaneds
Gtk::VBox* sources_pane_vbox = Gtk::manage(new Gtk::VBox());
sources_pane_vbox->pack_start(m_sources_pane, Gtk::PACK_EXPAND_WIDGET);
sources_pane_vbox->pack_start(m_progress_pane, Gtk::PACK_SHRINK);
Gtk::HPaned* browser_pane_sizer = Gtk::manage(new Gtk::HPaned());
browser_pane_sizer->add1(m_browser_pane);
browser_pane_sizer->add2(m_tab_pane);
browser_pane_sizer->set_position(200);
Gtk::HPaned* main_pane_sizer = Gtk::manage(new Gtk::HPaned());
main_pane_sizer->add1(*sources_pane_vbox);
main_pane_sizer->add2(*browser_pane_sizer);
main_pane_sizer->set_position(160);
pMainVBox->pack_start(*main_pane_sizer, Gtk::PACK_EXPAND_WIDGET, 2);
// Add statusbar
Gtk::Statusbar* pStatusbar = Gtk::manage(new Gtk::Statusbar());
pMainVBox->pack_end(*pStatusbar, Gtk::PACK_SHRINK);
}
void MainWindow::set_shuffle(ShuffleType type)
{
PlaybackManager::set_shuffle(type);
if (type != SHUFFLE_NONE) {
// TODO reactivate this after upgrading gtkmm, okay it didn't work
//m_refActionGroup->get_action("NextTrack")->property_icon_name().set_value(
// Glib::ustring("media-playlist-shuffle"));
//cout << "shuffle: " << endl;
//Glib::RefPtr<Gdk::Pixbuf> icon = Gtk::IconTheme::get_default()->load_icon(
// "media-playlist-shuffle", 24);
//Glib::RefPtr<Gtk::Action> action = m_refActionGroup->get_action("NextTrack");
//action->property_stock_id();
//m_refActionGroup->get_action("NextTrack")->set_property("icon-name",
// Glib::ustring("media-playlist-shuffle"));
}
//cout << "shuffle: " << shuffle_all << shuffle_artists << shuffle_albums << endl;
}
void MainWindow::on_play()
{
if (PlaybackManager::is_paused()) {
PlaybackManager::unpause();
} else {
PlaybackManager::pause();
}
// TODO this will crash if we click play without previously pausing something
}
void MainWindow::on_media_stopped(PlaySourceRef playlist, int index, bool is_paused)
{
Glib::RefPtr<Gtk::Action>::cast_static(
m_refActionGroup->get_action("PlayPause"))->property_stock_id().set_value(Gtk::Stock::MEDIA_PLAY);
}
void MainWindow::on_media_started(PlaySourceRef playlist, int index, bool was_paused)
{
Glib::RefPtr<Gtk::Action>::cast_static(
m_refActionGroup->get_action("PlayPause"))->property_stock_id().set_value(Gtk::Stock::MEDIA_PAUSE);
}
void MainWindow::on_menu_file_quit()
{
Gtk::Main::quit();
}
void MainWindow::on_menu_file_close()
{
hide();
}
void MainWindow::on_menu_file_refresh()
{
m_progress_pane.show();
for (std::vector<LibraryRef>::const_iterator iter = Database::get_libraries().begin();
iter != Database::get_libraries().end(); iter++) {
m_progress_pane.set_main_text(Glib::ustring::compose("Refreshing '%1'...", (*iter)->get_name()));
sigc::connection con = (*iter)->signal_refesh_pulse().connect(
sigc::mem_fun(m_progress_pane, &ProgressPane::pulse));
(*iter)->refresh();
con.disconnect();
}
m_progress_pane.hide();
}
void MainWindow::on_status_icon_click()
{
if(is_visible()) {
hide();
} else {
show();
}
}
void MainWindow::on_status_icon_popup(guint button, guint32 time)
{
Gtk::Menu* pMenu = static_cast<Gtk::Menu*>(
m_refUIManager->get_widget("/StatusIconPopup"));
m_refStatusIcon->popup_menu_at_position(*pMenu, button, time);
}
void MainWindow::show_libnotify_popup(PlaySourceRef playlist, int index, bool was_paused)
{
// TODO the tooltip that this sets for the status icon doesn't get unset when a song isn't playing
// TODO when the mouse keeps the linotify popup open and the song stops, is there a way to close
// the popup if another song isn't about to play?
// TODO this should only pop up if the user has not manually started a track
const Glib::ustring& summary = playlist->get_track(index)->get_title();
// TODO this body doesn't display in the popup if it contains a unicode character (i.e. Tiesto)
Glib::ustring body = Glib::ustring::compose("by <i>%1</i> from <i>%2</i>",
playlist->get_track(index)->get_artist(), playlist->get_track(index)->get_album());
m_refStatusIcon->set_tooltip(
playlist->get_track(index)->get_title()
+ "\nby " + playlist->get_track(index)->get_artist()
+ " from " + playlist->get_track(index)->get_album());
if (!m_notify) {
m_notify = notify_notification_new_with_status_icon(
summary.c_str(), body.c_str(), NULL, m_refStatusIcon->gobj());
notify_notification_add_action(m_notify, "previous", "Previous",
&MainWindow::libnotify_action_callback, NULL, NULL);
notify_notification_add_action(m_notify, "next", "Next",
&MainWindow::libnotify_action_callback, NULL, NULL);
}
else
notify_notification_update(m_notify,
summary.c_str(), body.c_str(), NULL);
notify_notification_show(m_notify, NULL);
// TODO do something with the libnotify error?
}
void MainWindow::libnotify_action_callback(
NotifyNotification* notify, gchar* action_id, gpointer user_data)
{
if (Glib::ustring(action_id) == "previous")
PlaybackManager::prev();
else
PlaybackManager::next();
}
void MainWindow::on_view_library_pane()
{
bool show_library = Glib::RefPtr<Gtk::ToggleAction>::cast_static(
m_refActionGroup->get_action("ViewBrowser"))->get_active();
if (show_library) {
m_browser_pane.show();
} else {
m_browser_pane.hide();
}
}