/*************************************************************************** * Copyright (C) 2008-2009 by Andrzej Rybczak * * electricityispower@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include #include #include #include #include #include "mpdpp.h" #include "ncmpcpp.h" #include "browser.h" #include "charset.h" #include "clock.h" #include "display.h" #include "global.h" #include "help.h" #include "helpers.h" #include "media_library.h" #include "lyrics.h" #include "playlist.h" #include "playlist_editor.h" #include "search_engine.h" #include "settings.h" #include "song.h" #include "info.h" #include "status_checker.h" #include "tag_editor.h" #define CHECK_MPD_MUSIC_DIR \ if (Config.mpd_music_dir.empty()) \ { \ ShowMessage("configuration variable mpd_music_dir is not set!"); \ continue; \ } using namespace Global; using namespace MPD; using std::make_pair; using std::string; using std::vector; ncmpcpp_config Global::Config; ncmpcpp_keys Global::Key; Window *Global::wCurrent; Window *Global::wPrev; Window *Global::wHeader; Window *Global::wFooter; Connection *Global::Mpd; int Global::lock_statusbar_delay = -1; size_t Global::main_start_y; size_t Global::main_height; size_t Global::lyrics_scroll_begin = 0; time_t Global::timer; string Global::editor_browsed_dir = "/"; string Global::editor_highlighted_dir; string Global::info_title; NcmpcppScreen Global::current_screen; NcmpcppScreen Global::prev_screen; bool Global::dont_change_now_playing = 0; bool Global::block_progressbar_update = 0; bool Global::block_playlist_update = 0; bool Global::block_item_list_update = 0; bool Global::messages_allowed = 0; bool Global::redraw_header = 1; bool Global::reload_lyrics = 0; vector Global::vFoundPositions; int Global::found_pos = 0; int main(int argc, char *argv[]) { CreateConfigDir(); DefaultConfiguration(Config); DefaultKeys(Key); ReadConfiguration(Config); ReadKeys(Key); Mpd = new Connection; if (getenv("MPD_HOST")) Mpd->SetHostname(getenv("MPD_HOST")); if (getenv("MPD_PORT")) Mpd->SetPort(atoi(getenv("MPD_PORT"))); if (Config.mpd_host != "localhost") Mpd->SetHostname(Config.mpd_host); if (Config.mpd_port != 6600) Mpd->SetPort(Config.mpd_port); Mpd->SetTimeout(Config.mpd_connection_timeout); if (argc > 1) { ParseArgv(argc, argv); } if (!ConnectToMPD()) return -1; // redirect std::cerr output to ~/.ncmpcpp/error.log file std::ofstream errorlog((config_dir + "error.log").c_str(), std::ios::app); std::streambuf *cerr_buffer = std::cerr.rdbuf(); std::cerr.rdbuf(errorlog.rdbuf()); InitScreen(Config.colors_enabled); init_current_locale(); main_start_y = 2; main_height = LINES-4; if (!Config.header_visibility) { main_start_y -= 2; main_height += 2; } if (!Config.statusbar_visibility) main_height++; myPlaylist->Init(); myBrowser->Init(); mySearcher->Init(); myLibrary->Init(); myPlaylistEditor->Init(); # ifdef HAVE_TAGLIB_H myTinyTagEditor->Init(); TagEditor::Init(); # endif // HAVE_TAGLIB_H # ifdef ENABLE_CLOCK Clock::Init(); # endif // ENABLE_CLOCK Help::Init(); Info::Init(); Lyrics::Init(); if (Config.header_visibility) { wHeader = new Window(0, 0, COLS, 1, "", Config.header_color, brNone); wHeader->Display(); } size_t footer_start_y = LINES-(Config.statusbar_visibility ? 2 : 1); size_t footer_height = Config.statusbar_visibility ? 2 : 1; wFooter = new Window(0, footer_start_y, COLS, footer_height, "", Config.statusbar_color, brNone); wFooter->SetTimeout(ncmpcpp_window_timeout); wFooter->SetGetStringHelper(TraceMpdStatus); wFooter->Display(); wCurrent = myPlaylist->Main(); current_screen = csPlaylist; timer = time(NULL); Mpd->SetStatusUpdater(NcmpcppStatusChanged, NULL); Mpd->SetErrorHandler(NcmpcppErrorCallback, NULL); // local variables int input; bool main_exit = 0; bool title_allowed = !Config.display_screens_numbers_on_start; string screen_title; timeval now, past; // local variables end signal(SIGPIPE, SIG_IGN); gettimeofday(&now, 0); while (!main_exit) { if (!Mpd->Connected()) { ShowMessage("Attempting to reconnect..."); if (Mpd->Connect()) ShowMessage("Connected!"); messages_allowed = 0; } TraceMpdStatus(); block_item_list_update = 0; block_playlist_update = 0; messages_allowed = 1; // header stuff gettimeofday(&past, 0); const size_t max_allowed_title_length = wHeader ? wHeader->GetWidth()-volume_state.length() : 0; if (((past.tv_sec == now.tv_sec && past.tv_usec >= now.tv_usec+500000) || past.tv_sec > now.tv_sec) && ((current_screen == csBrowser && myBrowser->CurrentDir().length() > max_allowed_title_length) || current_screen == csLyrics)) { redraw_header = 1; gettimeofday(&now, 0); } if (Config.header_visibility && redraw_header) { switch (current_screen) { case csHelp: screen_title = "Help"; break; case csPlaylist: screen_title = myPlaylist->Title(); break; case csBrowser: screen_title = myBrowser->Title(); break; # ifdef HAVE_TAGLIB_H case csTinyTagEditor: case csTagEditor: screen_title = "Tag editor"; break; # endif // HAVE_TAGLIB_H case csInfo: screen_title = info_title; break; case csSearcher: screen_title = mySearcher->Title(); break; case csLibrary: screen_title = myLibrary->Title(); break; case csLyrics: screen_title = "Lyrics: "; break; case csPlaylistEditor: screen_title = myPlaylistEditor->Title(); break; # ifdef ENABLE_CLOCK case csClock: screen_title = "Clock"; break; # endif // ENABLE_CLOCK default: break; } if (title_allowed) { wHeader->Bold(1); wHeader->WriteXY(0, 0, 1, "%s", screen_title.c_str()); wHeader->Bold(0); if (current_screen == csLyrics) { wHeader->Bold(1); *wHeader << Scroller(lyrics_song.toString("%a - %t"), max_allowed_title_length, lyrics_scroll_begin); wHeader->Bold(0); } } else { *wHeader << XY(0, 0) << fmtBold << (char)Key.Help[0] << fmtBoldEnd << ":Help " << fmtBold << (char)Key.Playlist[0] << fmtBoldEnd << ":Playlist " << fmtBold << (char)Key.Browser[0] << fmtBoldEnd << ":Browse " << fmtBold << (char)Key.SearchEngine[0] << fmtBoldEnd << ":Search " << fmtBold << (char)Key.MediaLibrary[0] << fmtBoldEnd << ":Library " << fmtBold << (char)Key.PlaylistEditor[0] << fmtBoldEnd << ":Playlist editor"; # ifdef HAVE_TAGLIB_H *wHeader << " " << fmtBold << (char)Key.TagEditor[0] << fmtBoldEnd << ":Tag editor"; # endif // HAVE_TAGLIB_H # ifdef ENABLE_CLOCK *wHeader << " " << fmtBold << (char)Key.Clock[0] << fmtBoldEnd << ":Clock"; # endif // ENABLE_CLOCK } wHeader->SetColor(Config.volume_color); wHeader->WriteXY(wHeader->GetWidth()-volume_state.length(), 0, 0, "%s", volume_state.c_str()); wHeader->SetColor(Config.header_color); wHeader->Refresh(); redraw_header = 0; } // header stuff end if (current_screen == csHelp || current_screen == csPlaylist || current_screen == csBrowser); else if (current_screen == csLibrary) { myLibrary->Update(); } else if (current_screen == csPlaylistEditor) { myPlaylistEditor->Update(); } else # ifdef HAVE_TAGLIB_H if (current_screen == csTagEditor) { TagEditor::Update(); } else # endif // HAVE_TAGLIB_H # ifdef ENABLE_CLOCK if (current_screen == csClock) { Clock::Update(); } else # endif if (current_screen == csLyrics) { Lyrics::Update(); } # ifdef HAVE_CURL_CURL_H if (!Info::Ready()) Lyrics::Ready(); # endif wCurrent->Display(); // redraw_screen = 0; wCurrent->ReadKey(input); if (input == ERR) continue; if (!title_allowed) redraw_header = 1; title_allowed = 1; timer = time(NULL); switch (current_screen) { case csPlaylist: myPlaylist->Main()->Highlighting(1); break; case csLibrary: case csPlaylistEditor: # ifdef HAVE_TAGLIB_H case csTagEditor: # endif // HAVE_TAGLIB_H { if (Keypressed(input, Key.Up) || Keypressed(input, Key.Down) || Keypressed(input, Key.PageUp) || Keypressed(input, Key.PageDown) || Keypressed(input, Key.Home) || Keypressed(input, Key.End) || Keypressed(input, Key.FindForward) || Keypressed(input, Key.FindBackward) || Keypressed(input, Key.NextFoundPosition) || Keypressed(input, Key.PrevFoundPosition)) { if (wCurrent == myLibrary->Artists) { myLibrary->Albums->Clear(0); myLibrary->Songs->Clear(0); } else if (wCurrent == myLibrary->Albums) { myLibrary->Songs->Clear(0); } else if (wCurrent == myPlaylistEditor->List) { myPlaylistEditor->Content->Clear(0); } # ifdef HAVE_TAGLIB_H else if (wCurrent == mEditorLeftCol) { mEditorTags->Clear(0); mEditorTagTypes->Refresh(); } // else if (wCurrent == mEditorTagTypes) // redraw_screen = 1; # endif // HAVE_TAGLIB_H } } default: break; } // key mapping beginning if ( Keypressed(input, Key.Up) #ifdef ENABLE_CLOCK && current_screen != csClock # endif // ENABLE_CLOCK ) { if (!Config.fancy_scrolling && (wCurrent == myLibrary->Artists || wCurrent == myPlaylistEditor->List # ifdef HAVE_TAGLIB_H || wCurrent == mEditorLeftCol # endif // HAVE_TAGLIB_H ) ) { wCurrent->SetTimeout(50); while (Keypressed(input, Key.Up)) { wCurrent->Scroll(wUp); wCurrent->Refresh(); wCurrent->ReadKey(input); } wCurrent->SetTimeout(ncmpcpp_window_timeout); } else wCurrent->Scroll(wUp); } else if ( Keypressed(input, Key.Down) #ifdef ENABLE_CLOCK && current_screen != csClock # endif // ENABLE_CLOCK ) { if (!Config.fancy_scrolling && (wCurrent == myLibrary->Artists || wCurrent == myPlaylistEditor->List # ifdef HAVE_TAGLIB_H || wCurrent == mEditorLeftCol # endif // HAVE_TAGLIB_H ) ) { wCurrent->SetTimeout(50); while (Keypressed(input, Key.Down)) { wCurrent->Scroll(wDown); wCurrent->Refresh(); wCurrent->ReadKey(input); } wCurrent->SetTimeout(ncmpcpp_window_timeout); } else wCurrent->Scroll(wDown); } else if ( Keypressed(input, Key.PageUp) #ifdef ENABLE_CLOCK && current_screen != csClock # endif // ENABLE_CLOCK ) { wCurrent->Scroll(wPageUp); } else if ( Keypressed(input, Key.PageDown) #ifdef ENABLE_CLOCK && current_screen != csClock # endif // ENABLE_CLOCK ) { wCurrent->Scroll(wPageDown); } else if (Keypressed(input, Key.Home)) { wCurrent->Scroll(wHome); } else if (Keypressed(input, Key.End)) { wCurrent->Scroll(wEnd); } else if (input == KEY_RESIZE) { // redraw_screen = 1; redraw_header = 1; if (COLS < 20 || LINES < 5) { endwin(); std::cout << "Screen too small!\n"; return 1; } main_height = LINES-4; if (!Config.header_visibility) main_height += 2; if (!Config.statusbar_visibility) main_height++; Help::Resize(); myPlaylist->Resize(); myBrowser->Resize(); mySearcher->Resize(); myLibrary->Resize(); myPlaylistEditor->Resize(); Info::Resize(); Lyrics::Resize(); # ifdef HAVE_TAGLIB_H myTinyTagEditor->Resize(); TagEditor::Resize(); # endif // HAVE_TAGLIB_H # ifdef ENABLE_CLOCK Clock::Resize(); # endif // ENABLE_CLOCK if (Config.header_visibility) wHeader->Resize(COLS, wHeader->GetHeight()); footer_start_y = LINES-(Config.statusbar_visibility ? 2 : 1); wFooter->MoveTo(0, footer_start_y); wFooter->Resize(COLS, wFooter->GetHeight()); # ifdef HAVE_TAGLIB_H if (current_screen == csLibrary) { myLibrary->Refresh(); } else if (current_screen == csTagEditor) { TagEditor::Refresh(); } else # endif // HAVE_TAGLIB_H if (current_screen == csPlaylistEditor) { myPlaylistEditor->Refresh(); } header_update_status = 1; PlayerState mpd_state = Mpd->GetState(); StatusChanges changes; if (mpd_state == psPlay || mpd_state == psPause) changes.ElapsedTime = 1; // restore status else changes.PlayerState = 1; NcmpcppStatusChanged(Mpd, changes, NULL); } else if (Keypressed(input, Key.GoToParentDir)) { if (wCurrent == myBrowser->Main() && myBrowser->CurrentDir() != "/") { myBrowser->Main()->Reset(); myBrowser->EnterPressed(); } } else if (Keypressed(input, Key.Enter)) { switch (current_screen) { case csPlaylist: { if (!myPlaylist->Main()->Empty()) Mpd->PlayID(myPlaylist->Main()->Current().GetID()); break; } case csBrowser: { myBrowser->EnterPressed(); break; } # ifdef HAVE_TAGLIB_H case csTinyTagEditor: { myTinyTagEditor->EnterPressed(); break; } # endif // HAVE_TAGLIB_H case csSearcher: { mySearcher->EnterPressed(); break; } case csLibrary: { myLibrary->EnterPressed(); break; } case csPlaylistEditor: { myPlaylistEditor->EnterPressed(); break; } # ifdef HAVE_TAGLIB_H case csTagEditor: { TagEditor::EnterPressed(); break; } # endif // HAVE_TAGLIB_H default: break; } } else if (Keypressed(input, Key.Space)) { if (Config.space_selects || wCurrent == myPlaylist->Main() # ifdef HAVE_TAGLIB_H || wCurrent == mEditorTags # endif // HAVE_TAGLIB_H ) { if (wCurrent == myPlaylist->Main() # ifdef HAVE_TAGLIB_H || wCurrent == mEditorTags # endif // HAVE_TAGLIB_H || (wCurrent == myBrowser->Main() && ((Menu *)wCurrent)->Choice() >= (myBrowser->CurrentDir() != "/" ? 1 : 0)) || (wCurrent == mySearcher->Main() && !mySearcher->Main()->Current().first) || wCurrent == myLibrary->Songs || wCurrent == myPlaylistEditor->Content) { List *mList = (Menu *)wCurrent; if (mList->Empty()) continue; size_t i = mList->Choice(); mList->Select(i, !mList->isSelected(i)); wCurrent->Scroll(wDown); } } else { if (current_screen == csBrowser) { myBrowser->SpacePressed(); } else if (current_screen == csSearcher) { mySearcher->SpacePressed(); } else if (current_screen == csLibrary) { myLibrary->SpacePressed(); } else if (current_screen == csPlaylistEditor) { myPlaylistEditor->SpacePressed(); } # ifdef HAVE_TAGLIB_H else if (wCurrent == mEditorLeftCol) { Config.albums_in_tag_editor = !Config.albums_in_tag_editor; wCurrent = wTagEditorActiveCol = mEditorLeftCol = Config.albums_in_tag_editor ? mEditorAlbums : mEditorDirs; ShowMessage("Switched to %s view", Config.albums_in_tag_editor ? "albums" : "directories"); mEditorLeftCol->Display(); mEditorTags->Clear(0); // redraw_screen = 1; } # endif // HAVE_TAGLIB_H else if (current_screen == csLyrics) { Config.now_playing_lyrics = !Config.now_playing_lyrics; ShowMessage("Reload lyrics if song changes: %s", Config.now_playing_lyrics ? "On" : "Off"); } } } else if (Keypressed(input, Key.VolumeUp)) { if (current_screen == csLibrary && input == Key.VolumeUp[0]) { myLibrary->NextColumn(); } else if (current_screen == csPlaylistEditor && input == Key.VolumeUp[0]) { myPlaylistEditor->NextColumn(); } # ifdef HAVE_TAGLIB_H else if (current_screen == csTagEditor && input == Key.VolumeUp[0]) { CLEAR_FIND_HISTORY; if (wCurrent == mEditorLeftCol) { mEditorLeftCol->HighlightColor(Config.main_highlight_color); wCurrent->Refresh(); wCurrent = wTagEditorActiveCol = mEditorTagTypes; mEditorTagTypes->HighlightColor(Config.active_column_color); } else if (wCurrent == mEditorTagTypes && mEditorTagTypes->Choice() < 12 && !mEditorTags->Empty()) { mEditorTagTypes->HighlightColor(Config.main_highlight_color); wCurrent->Refresh(); wCurrent = wTagEditorActiveCol = mEditorTags; mEditorTags->HighlightColor(Config.active_column_color); } } # endif // HAVE_TAGLIB_H else Mpd->SetVolume(Mpd->GetVolume()+1); } else if (Keypressed(input, Key.VolumeDown)) { if (current_screen == csLibrary && input == Key.VolumeDown[0]) { myLibrary->PrevColumn(); } else if (current_screen == csPlaylistEditor && input == Key.VolumeDown[0]) { myPlaylistEditor->PrevColumn(); } # ifdef HAVE_TAGLIB_H else if (current_screen == csTagEditor && input == Key.VolumeDown[0]) { CLEAR_FIND_HISTORY; if (wCurrent == mEditorTags) { mEditorTags->HighlightColor(Config.main_highlight_color); wCurrent->Refresh(); wCurrent = wTagEditorActiveCol = mEditorTagTypes; mEditorTagTypes->HighlightColor(Config.active_column_color); } else if (wCurrent == mEditorTagTypes) { mEditorTagTypes->HighlightColor(Config.main_highlight_color); wCurrent->Refresh(); wCurrent = wTagEditorActiveCol = mEditorLeftCol; mEditorLeftCol->HighlightColor(Config.active_column_color); } } # endif // HAVE_TAGLIB_H else Mpd->SetVolume(Mpd->GetVolume()-1); } else if (Keypressed(input, Key.Delete)) { if (!myPlaylist->Main()->Empty() && current_screen == csPlaylist) { block_playlist_update = 1; if (myPlaylist->Main()->hasSelected()) { vector list; myPlaylist->Main()->GetSelected(list); for (vector::const_reverse_iterator it = list.rbegin(); it != ((const vector &)list).rend(); it++) { Mpd->QueueDeleteSong(*it); myPlaylist->Main()->DeleteOption(*it); } ShowMessage("Selected items deleted!"); // redraw_screen = 1; } else { dont_change_now_playing = 1; myPlaylist->Main()->SetTimeout(50); while (!myPlaylist->Main()->Empty() && Keypressed(input, Key.Delete)) { size_t id = myPlaylist->Main()->Choice(); TraceMpdStatus(); timer = time(NULL); if (size_t(myPlaylist->NowPlaying) > id) // needed for keeping proper myPlaylist->NowPlaying--; // position of now playing song. Mpd->QueueDeleteSong(id); myPlaylist->Main()->DeleteOption(id); myPlaylist->Main()->Refresh(); myPlaylist->Main()->ReadKey(input); } myPlaylist->Main()->SetTimeout(ncmpcpp_window_timeout); dont_change_now_playing = 0; } Mpd->CommitQueue(); } else if (current_screen == csBrowser || wCurrent == myPlaylistEditor->List) { LockStatusbar(); string name = wCurrent == myBrowser->Main() ? myBrowser->Main()->Current().name : myPlaylistEditor->List->Current(); if (current_screen != csBrowser || myBrowser->Main()->Current().type == itPlaylist) { Statusbar() << "Delete playlist " << name << " ? [y/n] "; curs_set(1); int in = 0; do { TraceMpdStatus(); wFooter->ReadKey(in); } while (in != 'y' && in != 'n'); if (in == 'y') { Mpd->DeletePlaylist(locale_to_utf_cpy(name)); ShowMessage("Playlist %s deleted!", name.c_str()); if (!Config.local_browser) myBrowser->GetDirectory("/"); } else ShowMessage("Aborted!"); curs_set(0); myPlaylistEditor->List->Clear(0); // make playlists list update itself } UnlockStatusbar(); } else if (wCurrent == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty()) { if (myPlaylistEditor->Content->hasSelected()) { vector list; myPlaylistEditor->Content->GetSelected(list); locale_to_utf(myPlaylistEditor->List->Current()); for (vector::const_reverse_iterator it = list.rbegin(); it != ((const vector &)list).rend(); it++) { Mpd->QueueDeleteFromPlaylist(myPlaylistEditor->List->Current(), *it); myPlaylistEditor->Content->DeleteOption(*it); } utf_to_locale(myPlaylistEditor->List->Current()); ShowMessage("Selected items deleted from playlist '%s'!", myPlaylistEditor->List->Current().c_str()); // redraw_screen = 1; } else { myPlaylistEditor->Content->SetTimeout(50); locale_to_utf(myPlaylistEditor->List->Current()); while (!myPlaylistEditor->Content->Empty() && Keypressed(input, Key.Delete)) { TraceMpdStatus(); timer = time(NULL); Mpd->QueueDeleteFromPlaylist(myPlaylistEditor->List->Current(), myPlaylistEditor->Content->Choice()); myPlaylistEditor->Content->DeleteOption(myPlaylistEditor->Content->Choice()); myPlaylistEditor->Content->Refresh(); myPlaylistEditor->Content->ReadKey(input); } utf_to_locale(myPlaylistEditor->List->Current()); myPlaylistEditor->Content->SetTimeout(ncmpcpp_window_timeout); } Mpd->CommitQueue(); } } else if (Keypressed(input, Key.Prev)) { Mpd->Prev(); } else if (Keypressed(input, Key.Next)) { Mpd->Next(); } else if (Keypressed(input, Key.Pause)) { Mpd->Pause(); } else if (Keypressed(input, Key.SavePlaylist)) { LockStatusbar(); Statusbar() << "Save playlist as: "; string playlist_name = wFooter->GetString(); string real_playlist_name = playlist_name; locale_to_utf(real_playlist_name); UnlockStatusbar(); if (playlist_name.find("/") != string::npos) { ShowMessage("Playlist name cannot contain slashes!"); continue; } if (!playlist_name.empty()) { if (Mpd->SavePlaylist(real_playlist_name)) { ShowMessage("Playlist saved as: %s", playlist_name.c_str()); myPlaylistEditor->List->Clear(0); // make playlist's list update itself } else { LockStatusbar(); Statusbar() << "Playlist already exists, overwrite: " << playlist_name << " ? [y/n] "; curs_set(1); int in = 0; messages_allowed = 0; while (in != 'y' && in != 'n') { Mpd->UpdateStatus(); wFooter->ReadKey(in); } messages_allowed = 1; if (in == 'y') { Mpd->DeletePlaylist(real_playlist_name); if (Mpd->SavePlaylist(real_playlist_name)) ShowMessage("Playlist overwritten!"); } else ShowMessage("Aborted!"); curs_set(0); myPlaylistEditor->List->Clear(0); // make playlist's list update itself UnlockStatusbar(); } } if (!Config.local_browser && myBrowser->CurrentDir() == "/" && !myBrowser->Main()->Empty()) myBrowser->GetDirectory(myBrowser->CurrentDir()); } else if (Keypressed(input, Key.Stop)) { Mpd->Stop(); } else if (Keypressed(input, Key.MvSongUp)) { if (current_screen == csPlaylist && !myPlaylist->Main()->Empty()) { block_playlist_update = 1; myPlaylist->Main()->SetTimeout(50); if (myPlaylist->Main()->hasSelected()) { vector list; myPlaylist->Main()->GetSelected(list); for (vector::iterator it = list.begin(); it != list.end(); it++) if (*it == size_t(myPlaylist->NowPlaying) && list.front() > 0) myPlaylist->Main()->BoldOption(myPlaylist->NowPlaying, 0); vector origs(list); while (Keypressed(input, Key.MvSongUp) && list.front() > 0) { TraceMpdStatus(); timer = time(NULL); for (vector::iterator it = list.begin(); it != list.end(); it++) { (*it)--; myPlaylist->Main()->Swap(*it, (*it)+1); } myPlaylist->Main()->Highlight(list[(list.size()-1)/2]); myPlaylist->Main()->Refresh(); myPlaylist->Main()->ReadKey(input); } for (size_t i = 0; i < list.size(); i++) Mpd->QueueMove(origs[i], list[i]); Mpd->CommitQueue(); } else { size_t from, to; from = to = myPlaylist->Main()->Choice(); // unbold now playing as if song changes during move, this won't be unbolded. if (to == size_t(myPlaylist->NowPlaying) && to > 0) myPlaylist->Main()->BoldOption(myPlaylist->NowPlaying, 0); while (Keypressed(input, Key.MvSongUp) && to > 0) { TraceMpdStatus(); timer = time(NULL); to--; myPlaylist->Main()->Swap(to, to+1); myPlaylist->Main()->Scroll(wUp); myPlaylist->Main()->Refresh(); myPlaylist->Main()->ReadKey(input); } Mpd->Move(from, to); } myPlaylist->Main()->SetTimeout(ncmpcpp_window_timeout); } else if (wCurrent == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty()) { myPlaylistEditor->Content->SetTimeout(50); if (myPlaylistEditor->Content->hasSelected()) { vector list; myPlaylistEditor->Content->GetSelected(list); vector origs(list); while (Keypressed(input, Key.MvSongUp) && list.front() > 0) { TraceMpdStatus(); timer = time(NULL); for (vector::iterator it = list.begin(); it != list.end(); it++) { (*it)--; myPlaylistEditor->Content->Swap(*it, (*it)+1); } myPlaylistEditor->Content->Highlight(list[(list.size()-1)/2]); myPlaylistEditor->Content->Refresh(); myPlaylistEditor->Content->ReadKey(input); } for (size_t i = 0; i < list.size(); i++) if (origs[i] != list[i]) Mpd->QueueMove(myPlaylistEditor->List->Current(), origs[i], list[i]); Mpd->CommitQueue(); } else { size_t from, to; from = to = myPlaylistEditor->Content->Choice(); while (Keypressed(input, Key.MvSongUp) && to > 0) { TraceMpdStatus(); timer = time(NULL); to--; myPlaylistEditor->Content->Swap(to, to+1); myPlaylistEditor->Content->Scroll(wUp); myPlaylistEditor->Content->Refresh(); myPlaylistEditor->Content->ReadKey(input); } if (from != to) Mpd->Move(myPlaylistEditor->List->Current(), from, to); } myPlaylistEditor->Content->SetTimeout(ncmpcpp_window_timeout); } } else if (Keypressed(input, Key.MvSongDown)) { if (current_screen == csPlaylist && !myPlaylist->Main()->Empty()) { block_playlist_update = 1; myPlaylist->Main()->SetTimeout(50); if (myPlaylist->Main()->hasSelected()) { vector list; myPlaylist->Main()->GetSelected(list); for (vector::iterator it = list.begin(); it != list.end(); it++) if (*it == size_t(myPlaylist->NowPlaying) && list.back() < myPlaylist->Main()->Size()-1) myPlaylist->Main()->BoldOption(myPlaylist->NowPlaying, 0); vector origs(list); while (Keypressed(input, Key.MvSongDown) && list.back() < myPlaylist->Main()->Size()-1) { TraceMpdStatus(); timer = time(NULL); for (vector::reverse_iterator it = list.rbegin(); it != list.rend(); it++) { (*it)++; myPlaylist->Main()->Swap(*it, (*it)-1); } myPlaylist->Main()->Highlight(list[(list.size()-1)/2]); myPlaylist->Main()->Refresh(); myPlaylist->Main()->ReadKey(input); } for (int i = list.size()-1; i >= 0; i--) Mpd->QueueMove(origs[i], list[i]); Mpd->CommitQueue(); } else { size_t from, to; from = to = myPlaylist->Main()->Choice(); // unbold now playing as if song changes during move, this won't be unbolded. if (to == size_t(myPlaylist->NowPlaying) && to < myPlaylist->Main()->Size()-1) myPlaylist->Main()->BoldOption(myPlaylist->NowPlaying, 0); while (Keypressed(input, Key.MvSongDown) && to < myPlaylist->Main()->Size()-1) { TraceMpdStatus(); timer = time(NULL); to++; myPlaylist->Main()->Swap(to, to-1); myPlaylist->Main()->Scroll(wDown); myPlaylist->Main()->Refresh(); myPlaylist->Main()->ReadKey(input); } Mpd->Move(from, to); } myPlaylist->Main()->SetTimeout(ncmpcpp_window_timeout); } else if (wCurrent == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty()) { myPlaylistEditor->Content->SetTimeout(50); if (myPlaylistEditor->Content->hasSelected()) { vector list; myPlaylistEditor->Content->GetSelected(list); vector origs(list); while (Keypressed(input, Key.MvSongDown) && list.back() < myPlaylistEditor->Content->Size()-1) { TraceMpdStatus(); timer = time(NULL); for (vector::reverse_iterator it = list.rbegin(); it != list.rend(); it++) { (*it)++; myPlaylistEditor->Content->Swap(*it, (*it)-1); } myPlaylistEditor->Content->Highlight(list[(list.size()-1)/2]); myPlaylistEditor->Content->Refresh(); myPlaylistEditor->Content->ReadKey(input); } for (int i = list.size()-1; i >= 0; i--) if (origs[i] != list[i]) Mpd->QueueMove(myPlaylistEditor->List->Current(), origs[i], list[i]); Mpd->CommitQueue(); } else { size_t from, to; from = to = myPlaylistEditor->Content->Choice(); while (Keypressed(input, Key.MvSongDown) && to < myPlaylistEditor->Content->Size()-1) { TraceMpdStatus(); timer = time(NULL); to++; myPlaylistEditor->Content->Swap(to, to-1); myPlaylistEditor->Content->Scroll(wDown); myPlaylistEditor->Content->Refresh(); myPlaylistEditor->Content->ReadKey(input); } if (from != to) Mpd->Move(myPlaylistEditor->List->Current(), from, to); } myPlaylistEditor->Content->SetTimeout(ncmpcpp_window_timeout); } } else if (Keypressed(input, Key.Add)) { LockStatusbar(); Statusbar() << "Add: "; string path = wFooter->GetString(); UnlockStatusbar(); if (!path.empty()) { SongList list; Mpd->GetDirectoryRecursive(path, list); if (!list.empty()) { for (SongList::const_iterator it = list.begin(); it != list.end(); it++) Mpd->QueueAddSong(**it); if (Mpd->CommitQueue()) { Song &s = myPlaylist->Main()->at(myPlaylist->Main()->Size()-list.size()); if (s.GetHash() != list[0]->GetHash()) ShowMessage("%s", message_part_of_songs_added); } } else Mpd->AddSong(path); FreeSongList(list); } } else if (Keypressed(input, Key.SeekForward) || Keypressed(input, Key.SeekBackward)) { if (!myPlaylist->isPlaying()) continue; if (!myPlaylist->NowPlayingSong().GetTotalLength()) { ShowMessage("Unknown item length!"); continue; } block_progressbar_update = 1; LockStatusbar(); int songpos; time_t t = time(NULL); songpos = Mpd->GetElapsedTime(); const Song &s = myPlaylist->NowPlayingSong(); while (Keypressed(input, Key.SeekForward) || Keypressed(input, Key.SeekBackward)) { TraceMpdStatus(); timer = time(NULL); myPlaylist->Main()->ReadKey(input); int howmuch = Config.incremental_seeking ? (timer-t)/2+Config.seek_time : Config.seek_time; if (songpos < s.GetTotalLength() && Keypressed(input, Key.SeekForward)) { songpos += howmuch; if (songpos > s.GetTotalLength()) songpos = s.GetTotalLength(); } if (songpos < s.GetTotalLength() && songpos > 0 && Keypressed(input, Key.SeekBackward)) { songpos -= howmuch; if (songpos < 0) songpos = 0; } wFooter->Bold(1); string tracklength = "[" + Song::ShowTime(songpos) + "/" + s.GetLength() + "]"; wFooter->WriteXY(wFooter->GetWidth()-tracklength.length(), 1, 0, "%s", tracklength.c_str()); double progressbar_size = (double)songpos/(s.GetTotalLength()); int howlong = wFooter->GetWidth()*progressbar_size; mvwhline(wFooter->Raw(), 0, 0, 0, wFooter->GetWidth()); mvwhline(wFooter->Raw(), 0, 0, '=',howlong); mvwaddch(wFooter->Raw(), 0, howlong, '>'); wFooter->Bold(0); wFooter->Refresh(); } Mpd->Seek(songpos); block_progressbar_update = 0; UnlockStatusbar(); } else if (Keypressed(input, Key.ToggleDisplayMode)) { if (wCurrent == myPlaylist->Main()) { Config.columns_in_playlist = !Config.columns_in_playlist; ShowMessage("Playlist display mode: %s", Config.columns_in_playlist ? "Columns" : "Classic"); myPlaylist->Main()->SetItemDisplayer(Config.columns_in_playlist ? Display::SongsInColumns : Display::Songs); myPlaylist->Main()->SetItemDisplayerUserData(Config.columns_in_playlist ? &Config.song_columns_list_format : &Config.song_list_format); myPlaylist->Main()->SetTitle(Config.columns_in_playlist ? Display::Columns(Config.song_columns_list_format) : ""); } else if (wCurrent == myBrowser->Main()) { Config.columns_in_browser = !Config.columns_in_browser; ShowMessage("Browser display mode: %s", Config.columns_in_browser ? "Columns" : "Classic"); myBrowser->Main()->SetTitle(Config.columns_in_browser ? Display::Columns(Config.song_columns_list_format) : ""); } else if (wCurrent == mySearcher->Main()) { Config.columns_in_search_engine = !Config.columns_in_search_engine; ShowMessage("Search engine display mode: %s", Config.columns_in_search_engine ? "Columns" : "Classic"); if (mySearcher->Main()->Size() > SearchEngine::StaticOptions) mySearcher->Main()->SetTitle(Config.columns_in_search_engine ? Display::Columns(Config.song_columns_list_format) : ""); } // redraw_screen = 1; } # ifdef HAVE_CURL_CURL_H else if (Keypressed(input, Key.ToggleLyricsDB)) { const char *current_lyrics_plugin = GetLyricsPluginName(++Config.lyrics_db); if (!current_lyrics_plugin) { current_lyrics_plugin = GetLyricsPluginName(Config.lyrics_db = 0); } ShowMessage("Using lyrics database: %s", current_lyrics_plugin); } # endif // HAVE_CURL_CURL_H else if (Keypressed(input, Key.ToggleAutoCenter)) { Config.autocenter_mode = !Config.autocenter_mode; ShowMessage("Auto center mode: %s", Config.autocenter_mode ? "On" : "Off"); if (Config.autocenter_mode && myPlaylist->isPlaying()) myPlaylist->Main()->Highlight(myPlaylist->NowPlaying); } else if (Keypressed(input, Key.UpdateDB)) { if (current_screen == csBrowser) Mpd->UpdateDirectory(myBrowser->CurrentDir()); # ifdef HAVE_TAGLIB_H else if (current_screen == csTagEditor && !Config.albums_in_tag_editor) Mpd->UpdateDirectory(editor_browsed_dir); # endif // HAVE_TAGLIB_H else Mpd->UpdateDirectory("/"); } else if (Keypressed(input, Key.GoToNowPlaying)) { if (current_screen == csPlaylist && myPlaylist->isPlaying()) myPlaylist->Main()->Highlight(myPlaylist->NowPlaying); } else if (Keypressed(input, Key.ToggleRepeat)) { Mpd->SetRepeat(!Mpd->GetRepeat()); } else if (Keypressed(input, Key.ToggleRepeatOne)) { Config.repeat_one_mode = !Config.repeat_one_mode; ShowMessage("'Repeat one' mode: %s", Config.repeat_one_mode ? "On" : "Off"); } else if (Keypressed(input, Key.Shuffle)) { Mpd->Shuffle(); } else if (Keypressed(input, Key.ToggleRandom)) { Mpd->SetRandom(!Mpd->GetRandom()); } else if (Keypressed(input, Key.ToggleCrossfade)) { Mpd->SetCrossfade(Mpd->GetCrossfade() ? 0 : Config.crossfade_time); } else if (Keypressed(input, Key.SetCrossfade)) { LockStatusbar(); Statusbar() << "Set crossfade to: "; string crossfade = wFooter->GetString(3); UnlockStatusbar(); int cf = StrToInt(crossfade); if (cf > 0) { Config.crossfade_time = cf; Mpd->SetCrossfade(cf); } } else if (Keypressed(input, Key.EditTags)) { CHECK_MPD_MUSIC_DIR; # ifdef HAVE_TAGLIB_H if (wCurrent == myLibrary->Artists) { LockStatusbar(); Statusbar() << fmtBold << IntoStr(Config.media_lib_primary_tag) << fmtBoldEnd << ": "; string new_tag = wFooter->GetString(myLibrary->Artists->Current()); UnlockStatusbar(); if (!new_tag.empty() && new_tag != myLibrary->Artists->Current()) { bool success = 1; SongList list; ShowMessage("Updating tags..."); Mpd->StartSearch(1); Mpd->AddSearch(Config.media_lib_primary_tag, locale_to_utf_cpy(myLibrary->Artists->Current())); Mpd->CommitSearch(list); SongSetFunction set = IntoSetFunction(Config.media_lib_primary_tag); if (!set) continue; for (SongList::iterator it = list.begin(); it != list.end(); it++) { (*it)->Localize(); ((*it)->*set)(new_tag); ShowMessage("Updating tags in '%s'...", (*it)->GetName().c_str()); string path = Config.mpd_music_dir + (*it)->GetFile(); if (!WriteTags(**it)) { ShowMessage("Error updating tags in '%s'!", (*it)->GetFile().c_str()); success = 0; break; } } if (success) { Mpd->UpdateDirectory(FindSharedDir(list)); ShowMessage("Tags updated succesfully!"); } FreeSongList(list); } } else if (wCurrent == myLibrary->Albums) { LockStatusbar(); Statusbar() << fmtBold << "Album: " << fmtBoldEnd; string new_album = wFooter->GetString(myLibrary->Albums->Current().second); UnlockStatusbar(); if (!new_album.empty() && new_album != myLibrary->Albums->Current().second) { bool success = 1; ShowMessage("Updating tags..."); for (size_t i = 0; i < myLibrary->Songs->Size(); i++) { (*myLibrary->Songs)[i].Localize(); ShowMessage("Updating tags in '%s'...", (*myLibrary->Songs)[i].GetName().c_str()); string path = Config.mpd_music_dir + (*myLibrary->Songs)[i].GetFile(); TagLib::FileRef f(path.c_str()); if (f.isNull()) { ShowMessage("Error opening file '%s'!", (*myLibrary->Songs)[i].GetFile().c_str()); success = 0; break; } f.tag()->setAlbum(ToWString(new_album)); if (!f.save()) { ShowMessage("Error writing tags in '%s'!", (*myLibrary->Songs)[i].GetFile().c_str()); success = 0; break; } } if (success) { Mpd->UpdateDirectory(FindSharedDir(myLibrary->Songs)); ShowMessage("Tags updated succesfully!"); } } } else if ( (wCurrent == myPlaylist->Main() && !myPlaylist->Main()->Empty()) || (wCurrent == myBrowser->Main() && myBrowser->Main()->Current().type == itSong) || (wCurrent == mySearcher->Main() && !mySearcher->Main()->Current().first) || (wCurrent == myLibrary->Songs && !myLibrary->Songs->Empty()) || (wCurrent == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty()) || (wCurrent == mEditorTags && !mEditorTags->Empty())) { myTinyTagEditor->SwitchTo(); } else if (wCurrent == mEditorDirs) { string old_dir = mEditorDirs->Current().first; LockStatusbar(); Statusbar() << fmtBold << "Directory: " << fmtBoldEnd; string new_dir = wFooter->GetString(old_dir); UnlockStatusbar(); if (!new_dir.empty() && new_dir != old_dir) { string full_old_dir = Config.mpd_music_dir + editor_browsed_dir + "/" + locale_to_utf_cpy(old_dir); string full_new_dir = Config.mpd_music_dir + editor_browsed_dir + "/" + locale_to_utf_cpy(new_dir); if (rename(full_old_dir.c_str(), full_new_dir.c_str()) == 0) { Mpd->UpdateDirectory(editor_browsed_dir); } else { ShowMessage("Cannot rename '%s' to '%s'!", old_dir.c_str(), new_dir.c_str()); } } } else # endif // HAVE_TAGLIB_H if (wCurrent == myBrowser->Main() && myBrowser->Main()->Current().type == itDirectory) { string old_dir = myBrowser->Main()->Current().name; LockStatusbar(); Statusbar() << fmtBold << "Directory: " << fmtBoldEnd; string new_dir = wFooter->GetString(old_dir); UnlockStatusbar(); if (!new_dir.empty() && new_dir != old_dir) { string full_old_dir; if (!Config.local_browser) full_old_dir += Config.mpd_music_dir; full_old_dir += locale_to_utf_cpy(old_dir); string full_new_dir; if (!Config.local_browser) full_new_dir += Config.mpd_music_dir; full_new_dir += locale_to_utf_cpy(new_dir); int rename_result = rename(full_old_dir.c_str(), full_new_dir.c_str()); if (rename_result == 0) { ShowMessage("'%s' renamed to '%s'", old_dir.c_str(), new_dir.c_str()); if (!Config.local_browser) Mpd->UpdateDirectory(FindSharedDir(old_dir, new_dir)); myBrowser->GetDirectory(myBrowser->CurrentDir()); } else ShowMessage("Cannot rename '%s' to '%s'!", old_dir.c_str(), new_dir.c_str()); } } else if (wCurrent == myPlaylistEditor->List || (wCurrent == myBrowser->Main() && myBrowser->Main()->Current().type == itPlaylist)) { string old_name = wCurrent == myPlaylistEditor->List ? myPlaylistEditor->List->Current() : myBrowser->Main()->Current().name; LockStatusbar(); Statusbar() << fmtBold << "Playlist: " << fmtBoldEnd; string new_name = wFooter->GetString(old_name); UnlockStatusbar(); if (!new_name.empty() && new_name != old_name) { Mpd->Rename(locale_to_utf_cpy(old_name), locale_to_utf_cpy(new_name)); ShowMessage("Playlist '%s' renamed to '%s'", old_name.c_str(), new_name.c_str()); if (!Config.local_browser) myBrowser->GetDirectory("/"); myPlaylistEditor->List->Clear(0); } } } else if (Keypressed(input, Key.GoToContainingDir)) { if ((wCurrent == myPlaylist->Main() && !myPlaylist->Main()->Empty()) || (wCurrent == mySearcher->Main() && !mySearcher->Main()->Current().first) || (wCurrent == myLibrary->Songs && !myLibrary->Songs->Empty()) || (wCurrent == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty()) # ifdef HAVE_TAGLIB_H || (wCurrent == mEditorTags && !mEditorTags->Empty()) # endif // HAVE_TAGLIB_H ) { size_t id = ((Menu *)wCurrent)->Choice(); Song *s = 0; switch (current_screen) { case csPlaylist: s = &myPlaylist->Main()->at(id); break; case csSearcher: s = mySearcher->Main()->at(id).second; break; case csLibrary: s = &myLibrary->Songs->at(id); break; case csPlaylistEditor: s = &myPlaylistEditor->Content->at(id); break; # ifdef HAVE_TAGLIB_H case csTagEditor: s = &mEditorTags->at(id); break; # endif // HAVE_TAGLIB_H default: break; } if (s->GetDirectory().empty()) // for streams continue; Config.local_browser = !s->IsFromDB(); string option = s->toString(Config.song_status_format); locale_to_utf(option); myBrowser->GetDirectory(s->GetDirectory()); for (size_t i = 0; i < myBrowser->Main()->Size(); i++) { if (myBrowser->Main()->at(i).type == itSong && option == myBrowser->Main()->at(i).song->toString(Config.song_status_format)) { myBrowser->Main()->Highlight(i); break; } } myBrowser->SwitchTo(); } } else if (Keypressed(input, Key.StartSearching)) { if (wCurrent == mySearcher->Main()) { mySearcher->Main()->Highlight(SearchEngine::SearchButton); mySearcher->Main()->Highlighting(0); mySearcher->Main()->Refresh(); mySearcher->Main()->Highlighting(1); mySearcher->EnterPressed(); } } else if (Keypressed(input, Key.GoToPosition)) { if (!myPlaylist->isPlaying()) continue; if (!myPlaylist->NowPlayingSong().GetTotalLength()) { ShowMessage("Unknown item length!"); continue; } LockStatusbar(); Statusbar() << "Position to go (in %): "; string position = wFooter->GetString(3); int newpos = StrToInt(position); if (newpos > 0 && newpos < 100 && !position.empty()) Mpd->Seek(myPlaylist->NowPlayingSong().GetTotalLength()*newpos/100.0); UnlockStatusbar(); } else if (Keypressed(input, Key.ReverseSelection)) { if (wCurrent == myPlaylist->Main() || wCurrent == myBrowser->Main() || (wCurrent == mySearcher->Main() && !mySearcher->Main()->Current().first) || wCurrent == myLibrary->Songs || wCurrent == myPlaylistEditor->Content # ifdef HAVE_TAGLIB_H || wCurrent == mEditorTags # endif // HAVE_TAGLIB_H ) { List *mList = reinterpret_cast *>(wCurrent); for (size_t i = 0; i < mList->Size(); i++) mList->Select(i, !mList->isSelected(i) && !mList->isStatic(i)); // hackish shit begins if (wCurrent == myBrowser->Main() && myBrowser->CurrentDir() != "/") mList->Select(0, 0); // [..] cannot be selected, uhm. if (wCurrent == mySearcher->Main()) mList->Select(SearchEngine::ResetButton, 0); // 'Reset' cannot be selected, omgplz. // hacking shit ends. ShowMessage("Selection reversed!"); } } else if (Keypressed(input, Key.DeselectAll)) { if (wCurrent == myPlaylist->Main() || wCurrent == myBrowser->Main() || wCurrent == mySearcher->Main() || wCurrent == myLibrary->Songs || wCurrent == myPlaylistEditor->Content # ifdef HAVE_TAGLIB_H || wCurrent == mEditorTags # endif // HAVE_TAGLIB_H ) { List *mList = reinterpret_cast *>(wCurrent); if (mList->hasSelected()) { for (size_t i = 0; i < mList->Size(); i++) mList->Select(i, 0); ShowMessage("Items deselected!"); } } } else if (Keypressed(input, Key.AddSelected)) { if (wCurrent != myPlaylist->Main() && wCurrent != myBrowser->Main() && wCurrent != mySearcher->Main() && wCurrent != myLibrary->Songs && wCurrent != myPlaylistEditor->Content) continue; List *mList = reinterpret_cast *>(wCurrent); if (!mList->hasSelected()) { ShowMessage("No selected items!"); continue; } vector list; mList->GetSelected(list); SongList result; for (vector::const_iterator it = list.begin(); it != list.end(); it++) { switch (current_screen) { case csPlaylist: { Song *s = new Song(myPlaylist->Main()->at(*it)); result.push_back(s); break; } case csBrowser: { const Item &item = myBrowser->Main()->at(*it); switch (item.type) { case itDirectory: { Mpd->GetDirectoryRecursive(locale_to_utf_cpy(item.name), result); break; } case itSong: { Song *s = new Song(*item.song); result.push_back(s); break; } case itPlaylist: { Mpd->GetPlaylistContent(locale_to_utf_cpy(item.name), result); break; } } break; } case csSearcher: { Song *s = new Song(*mySearcher->Main()->at(*it).second); result.push_back(s); break; } case csLibrary: { Song *s = new Song(myLibrary->Songs->at(*it)); result.push_back(s); break; } case csPlaylistEditor: { Song *s = new Song(myPlaylistEditor->Content->at(*it)); result.push_back(s); break; } default: break; } } size_t dialog_width = COLS*0.8; size_t dialog_height = LINES*0.6; Menu *mDialog = new Menu((COLS-dialog_width)/2, (LINES-dialog_height)/2, dialog_width, dialog_height, "Add selected items to...", Config.main_color, Config.window_border); mDialog->SetTimeout(ncmpcpp_window_timeout); mDialog->SetItemDisplayer(Display::Generic); bool playlists_not_active = current_screen == csBrowser && Config.local_browser; if (playlists_not_active) { ShowMessage("Local items cannot be added to m3u playlist!"); } mDialog->AddOption("Current MPD playlist"); mDialog->AddOption("Create new playlist (m3u file)", 0, playlists_not_active); mDialog->AddSeparator(); TagList playlists; Mpd->GetPlaylists(playlists); for (TagList::iterator it = playlists.begin(); it != playlists.end(); it++) { utf_to_locale(*it); mDialog->AddOption("'" + *it + "' playlist", 0, playlists_not_active); } mDialog->AddSeparator(); mDialog->AddOption("Cancel"); mDialog->Display(); prev_screen = current_screen; current_screen = csOther; while (!Keypressed(input, Key.Enter)) { TraceMpdStatus(); mDialog->Refresh(); mDialog->ReadKey(input); if (Keypressed(input, Key.Up)) mDialog->Scroll(wUp); else if (Keypressed(input, Key.Down)) mDialog->Scroll(wDown); else if (Keypressed(input, Key.PageUp)) mDialog->Scroll(wPageUp); else if (Keypressed(input, Key.PageDown)) mDialog->Scroll(wPageDown); else if (Keypressed(input, Key.Home)) mDialog->Scroll(wHome); else if (Keypressed(input, Key.End)) mDialog->Scroll(wEnd); } current_screen = prev_screen; size_t id = mDialog->Choice(); // redraw_screen = 1; if (current_screen == csLibrary) { myLibrary->Refresh(); } else if (current_screen == csPlaylistEditor) { myPlaylistEditor->Refresh(); } else wCurrent->Refresh(); if (id == 0) { for (SongList::const_iterator it = result.begin(); it != result.end(); it++) Mpd->QueueAddSong(**it); if (Mpd->CommitQueue()) { ShowMessage("Selected items added!"); Song &s = myPlaylist->Main()->at(myPlaylist->Main()->Size()-result.size()); if (s.GetHash() != result[0]->GetHash()) ShowMessage("%s", message_part_of_songs_added); } } else if (id == 1) { LockStatusbar(); Statusbar() << "Save playlist as: "; string playlist = wFooter->GetString(); string real_playlist = playlist; locale_to_utf(real_playlist); UnlockStatusbar(); if (!playlist.empty()) { for (SongList::const_iterator it = result.begin(); it != result.end(); it++) Mpd->QueueAddToPlaylist(real_playlist, **it); Mpd->CommitQueue(); ShowMessage("Selected items added to playlist '%s'!", playlist.c_str()); } } else if (id > 1 && id < mDialog->Size()-1) { locale_to_utf(playlists[id-3]); for (SongList::const_iterator it = result.begin(); it != result.end(); it++) Mpd->QueueAddToPlaylist(playlists[id-3], **it); Mpd->CommitQueue(); utf_to_locale(playlists[id-3]); ShowMessage("Selected items added to playlist '%s'!", playlists[id-3].c_str()); } if (id != mDialog->Size()-1) { // refresh playlist's lists if (!Config.local_browser && myBrowser->CurrentDir() == "/") myBrowser->GetDirectory("/"); myPlaylistEditor->List->Clear(0); // make playlist editor update itself } timer = time(NULL); delete mDialog; FreeSongList(result); } else if (Keypressed(input, Key.Crop)) { if (myPlaylist->Main()->hasSelected()) { for (size_t i = 0; i < myPlaylist->Main()->Size(); i++) { if (!myPlaylist->Main()->isSelected(i) && i != size_t(myPlaylist->NowPlaying)) Mpd->QueueDeleteSongId(myPlaylist->Main()->at(i).GetID()); } // if mpd deletes now playing song deletion will be sluggishly slow // then so we have to assure it will be deleted at the very end. if (myPlaylist->isPlaying() && !myPlaylist->Main()->isSelected(myPlaylist->NowPlaying)) Mpd->QueueDeleteSongId(myPlaylist->NowPlayingSong().GetID()); ShowMessage("Deleting all items but selected..."); Mpd->CommitQueue(); ShowMessage("Items deleted!"); } else { if (!myPlaylist->isPlaying()) { ShowMessage("Nothing is playing now!"); continue; } for (int i = 0; i < myPlaylist->NowPlaying; i++) Mpd->QueueDeleteSongId(myPlaylist->Main()->at(i).GetID()); for (size_t i = myPlaylist->NowPlaying+1; i < myPlaylist->Main()->Size(); i++) Mpd->QueueDeleteSongId(myPlaylist->Main()->at(i).GetID()); ShowMessage("Deleting all items except now playing one..."); Mpd->CommitQueue(); ShowMessage("Items deleted!"); } } else if (Keypressed(input, Key.Clear)) { ShowMessage("Clearing playlist..."); Mpd->ClearPlaylist(); ShowMessage("Cleared playlist!"); } else if (Keypressed(input, Key.FindForward) || Keypressed(input, Key.FindBackward)) { if ((current_screen == csHelp || current_screen == csSearcher # ifdef HAVE_TAGLIB_H || current_screen == csTinyTagEditor || wCurrent == mEditorTagTypes # endif // HAVE_TAGLIB_H ) && (current_screen != csSearcher || mySearcher->Main()->Current().first) ) continue; string how = Keypressed(input, Key.FindForward) ? "forward" : "backward"; LockStatusbar(); Statusbar() << "Find " << how << ": "; string findme = wFooter->GetString(); UnlockStatusbar(); timer = time(NULL); if (findme.empty()) continue; ToLower(findme); CLEAR_FIND_HISTORY; ShowMessage("Searching..."); List *mList = reinterpret_cast *>(wCurrent); for (size_t i = (wCurrent == mySearcher->Main() ? SearchEngine::StaticOptions : 0); i < mList->Size(); i++) { string name; switch (current_screen) { case csPlaylist: name = myPlaylist->Main()->at(i).toString(Config.song_list_format); break; case csBrowser: switch (myBrowser->Main()->at(i).type) { case itDirectory: name = myBrowser->Main()->at(i).name; break; case itSong: name = myBrowser->Main()->at(i).song->toString(Config.song_list_format); break; case itPlaylist: name = Config.browser_playlist_prefix.Str(); name += myBrowser->Main()->at(i).name; break; } break; case csSearcher: name = mySearcher->Main()->at(i).second->toString(Config.song_list_format); break; case csLibrary: if (wCurrent == myLibrary->Artists) name = myLibrary->Artists->at(i); else if (wCurrent == myLibrary->Albums) name = myLibrary->Albums->at(i).first; else name = myLibrary->Songs->at(i).toString(Config.song_library_format); break; case csPlaylistEditor: if (wCurrent == myPlaylistEditor->List) name = myPlaylistEditor->List->at(i); else name = myPlaylistEditor->Content->at(i).toString(Config.song_list_format); break; # ifdef HAVE_TAGLIB_H case csTagEditor: if (wCurrent == mEditorLeftCol) name = mEditorLeftCol->at(i).first; else { const Song &s = mEditorTags->at(i); switch (mEditorTagTypes->Choice()) { case 0: name = s.GetTitle(); break; case 1: name = s.GetArtist(); break; case 2: name = s.GetAlbum(); break; case 3: name = s.GetYear(); break; case 4: name = s.GetTrack(); break; case 5: name = s.GetGenre(); break; case 6: name = s.GetComposer(); break; case 7: name = s.GetPerformer(); break; case 8: name = s.GetDisc(); break; case 9: name = s.GetComment(); break; case 11: if (s.GetNewName().empty()) name = s.GetName(); else { name = s.GetName(); name += " -> "; name += s.GetNewName(); } break; default: break; } } break; # endif // HAVE_TAGLIB_H default: break; } ToLower(name); if (name.find(findme) != string::npos && !mList->isStatic(i)) { vFoundPositions.push_back(i); if (Keypressed(input, Key.FindForward)) // forward { if (found_pos < 0 && i >= mList->Choice()) found_pos = vFoundPositions.size()-1; } else // backward { if (i <= mList->Choice()) found_pos = vFoundPositions.size()-1; } } } ShowMessage("Searching finished!"); if (Config.wrapped_search ? vFoundPositions.empty() : found_pos < 0) ShowMessage("Unable to find \"%s\"", findme.c_str()); else { mList->Highlight(vFoundPositions[found_pos < 0 ? 0 : found_pos]); if (wCurrent == myPlaylist->Main()) { timer = time(NULL); myPlaylist->Main()->Highlighting(1); } } } else if (Keypressed(input, Key.NextFoundPosition) || Keypressed(input, Key.PrevFoundPosition)) { if (!vFoundPositions.empty()) { List *mList = reinterpret_cast *>(wCurrent); try { mList->Highlight(vFoundPositions.at(Keypressed(input, Key.NextFoundPosition) ? ++found_pos : --found_pos)); } catch (std::out_of_range) { if (Config.wrapped_search) { if (Keypressed(input, Key.NextFoundPosition)) { mList->Highlight(vFoundPositions.front()); found_pos = 0; } else { mList->Highlight(vFoundPositions.back()); found_pos = vFoundPositions.size()-1; } } else found_pos = Keypressed(input, Key.NextFoundPosition) ? vFoundPositions.size()-1 : 0; } } } else if (Keypressed(input, Key.ToggleFindMode)) { Config.wrapped_search = !Config.wrapped_search; ShowMessage("Search mode: %s", Config.wrapped_search ? "Wrapped" : "Normal"); } else if (Keypressed(input, Key.ToggleSpaceMode)) { Config.space_selects = !Config.space_selects; ShowMessage("Space mode: %s item", Config.space_selects ? "Select/deselect" : "Add"); } else if (Keypressed(input, Key.ToggleAddMode)) { Config.ncmpc_like_songs_adding = !Config.ncmpc_like_songs_adding; ShowMessage("Add mode: %s", Config.ncmpc_like_songs_adding ? "Add item to playlist, remove if already added" : "Always add item to playlist"); } else if (Keypressed(input, Key.SwitchTagTypeList)) { if (wCurrent == myBrowser->Main()) { myBrowser->ChangeBrowseMode(); } else if (wCurrent == myLibrary->Artists) { LockStatusbar(); Statusbar() << "Tag type ? [" << fmtBold << 'a' << fmtBoldEnd << "rtist/" << fmtBold << 'y' << fmtBoldEnd << "ear/" << fmtBold << 'g' << fmtBoldEnd << "enre/" << fmtBold << 'c' << fmtBoldEnd << "omposer/" << fmtBold << 'p' << fmtBoldEnd << "erformer] "; int item; curs_set(1); do { TraceMpdStatus(); wFooter->ReadKey(item); } while (item != 'a' && item != 'y' && item != 'g' && item != 'c' && item != 'p'); curs_set(0); UnlockStatusbar(); mpd_TagItems new_tagitem = IntoTagItem(item); if (new_tagitem != Config.media_lib_primary_tag) { Config.media_lib_primary_tag = new_tagitem; string item_type = IntoStr(Config.media_lib_primary_tag); myLibrary->Artists->SetTitle(item_type + "s"); myLibrary->Artists->Reset(); myLibrary->Artists->Clear(0); myLibrary->Artists->Display(); ToLower(item_type); ShowMessage("Switched to list of %s tag", item_type.c_str()); } } } else if (Keypressed(input, Key.SongInfo)) { Info::GetSong(); } # ifdef HAVE_CURL_CURL_H else if (Keypressed(input, Key.ArtistInfo)) { Info::GetArtist(); } # endif // HAVE_CURL_CURL_H else if (Keypressed(input, Key.Lyrics)) { Lyrics::Get(); } else if (Keypressed(input, Key.Help)) { Help::SwitchTo(); } else if (Keypressed(input, Key.ScreenSwitcher)) { if (current_screen == csPlaylist) myBrowser->SwitchTo(); else myPlaylist->SwitchTo(); } else if (Keypressed(input, Key.Playlist)) { myPlaylist->SwitchTo(); } else if (Keypressed(input, Key.Browser)) { myBrowser->SwitchTo(); } else if (Keypressed(input, Key.SearchEngine)) { mySearcher->SwitchTo(); } else if (Keypressed(input, Key.MediaLibrary)) { myLibrary->SwitchTo(); } else if (Keypressed(input, Key.PlaylistEditor)) { myPlaylistEditor->SwitchTo(); } # ifdef HAVE_TAGLIB_H else if (Keypressed(input, Key.TagEditor)) { CHECK_MPD_MUSIC_DIR; TagEditor::SwitchTo(); } # endif // HAVE_TAGLIB_H # ifdef ENABLE_CLOCK else if (Keypressed(input, Key.Clock)) { Clock::SwitchTo(); } # endif // ENABLE_CLOCK else if (Keypressed(input, Key.Quit)) main_exit = 1; // key mapping end } // restore old cerr buffer std::cerr.rdbuf(cerr_buffer); errorlog.close(); Mpd->Disconnect(); DestroyScreen(); return 0; }