/*************************************************************************** * Copyright (C) 2008-2012 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 "actions.h" #include "charset.h" #include "config.h" #include "display.h" #include "global.h" #include "mpdpp.h" #include "utility/comparators.h" #include "browser.h" #include "clock.h" #include "help.h" #include "media_library.h" #include "lastfm.h" #include "lyrics.h" #include "playlist.h" #include "playlist_editor.h" #include "search_engine.h" #include "sel_items_adder.h" #include "server_info.h" #include "song_info.h" #include "outputs.h" #include "utility/string.h" #include "utility/type_conversions.h" #include "tag_editor.h" #include "tiny_tag_editor.h" #include "visualizer.h" using Global::myScreen; bool Action::OriginalStatusbarVisibility; bool Action::DesignChanged; bool Action::OrderResize; bool Action::ExitMainLoop = false; size_t Action::HeaderHeight; size_t Action::FooterHeight; size_t Action::FooterStartY; std::map Action::Actions; Action::Key Action::NoOp = Action::Key(ERR, ctNCurses); Action::Key Action::ReadKey(Window &w) { std::string tmp; int input; while (true) { input = w.ReadKey(); if (input == ERR) return NoOp; if (input > 255) return Key(input, ctNCurses); else { wchar_t wc; tmp += input; size_t conv_res = mbrtowc(&wc, tmp.c_str(), MB_CUR_MAX, 0); if (conv_res == size_t(-1)) // incomplete multibyte character continue; else if (conv_res == size_t(-2)) // garbage character sequence return NoOp; else // character complete return Key(wc, ctStandard); } } // not reachable assert(false); } void Action::ValidateScreenSize() { using Global::MainHeight; if (COLS < 20 || MainHeight < 3) { DestroyScreen(); std::cout << "Screen is too small!\n"; exit(1); } } void Action::SetResizeFlags() { myHelp->hasToBeResized = 1; myPlaylist->hasToBeResized = 1; myBrowser->hasToBeResized = 1; mySearcher->hasToBeResized = 1; myLibrary->hasToBeResized = 1; myPlaylistEditor->hasToBeResized = 1; myLyrics->hasToBeResized = 1; mySelectedItemsAdder->hasToBeResized = 1; mySongInfo->hasToBeResized = 1; # ifdef HAVE_CURL_CURL_H myLastfm->hasToBeResized = 1; # endif // HAVE_CURL_CURL_H # ifdef HAVE_TAGLIB_H myTinyTagEditor->hasToBeResized = 1; myTagEditor->hasToBeResized = 1; # endif // HAVE_TAGLIB_H # ifdef ENABLE_VISUALIZER myVisualizer->hasToBeResized = 1; # endif // ENABLE_VISUALIZER # ifdef ENABLE_OUTPUTS myOutputs->hasToBeResized = 1; # endif // ENABLE_OUTPUTS # ifdef ENABLE_CLOCK myClock->hasToBeResized = 1; # endif // ENABLE_CLOCK } void Action::ResizeScreen() { using Global::MainHeight; using Global::RedrawHeader; using Global::RedrawStatusbar; using Global::wHeader; using Global::wFooter; OrderResize = 0; # if defined(USE_PDCURSES) resize_term(0, 0); # else // update internal screen dimensions if (!DesignChanged) { endwin(); refresh(); // get rid of KEY_RESIZE as it sometimes // corrupts our new cool ReadKey() function // because KEY_RESIZE doesn't come from stdin // and thus select cannot detect it timeout(10); getch(); timeout(-1); } # endif RedrawHeader = true; MainHeight = LINES-(Config.new_design ? 7 : 4); ValidateScreenSize(); if (!Config.header_visibility) MainHeight += 2; if (!Config.statusbar_visibility) MainHeight++; SetResizeFlags(); ApplyToVisibleWindows(&BasicScreen::Resize); if (Config.header_visibility || Config.new_design) wHeader->Resize(COLS, HeaderHeight); FooterStartY = LINES-(Config.statusbar_visibility ? 2 : 1); wFooter->MoveTo(0, FooterStartY); wFooter->Resize(COLS, Config.statusbar_visibility ? 2 : 1); ApplyToVisibleWindows(&BasicScreen::Refresh); RedrawStatusbar = true; MPD::StatusChanges changes; if (!Mpd.isPlaying() || DesignChanged) { changes.PlayerState = 1; if (DesignChanged) changes.Volume = 1; } // Note: routines for drawing separator if alternative user // interface is active and header is hidden are placed in // NcmpcppStatusChanges.StatusFlags changes.StatusFlags = 1; // force status update NcmpcppStatusChanged(&Mpd, changes, 0); if (DesignChanged) { RedrawStatusbar = true; NcmpcppStatusChanged(&Mpd, MPD::StatusChanges(), 0); DesignChanged = 0; ShowMessage("User interface: %s", Config.new_design ? "Alternative" : "Classic"); } wFooter->Refresh(); refresh(); } void Action::SetWindowsDimensions() { using Global::MainStartY; using Global::MainHeight; MainStartY = Config.new_design ? 5 : 2; MainHeight = LINES-(Config.new_design ? 7 : 4); if (!Config.header_visibility) { MainStartY -= 2; MainHeight += 2; } if (!Config.statusbar_visibility) MainHeight++; HeaderHeight = Config.new_design ? (Config.header_visibility ? 5 : 3) : 1; FooterStartY = LINES-(Config.statusbar_visibility ? 2 : 1); FooterHeight = Config.statusbar_visibility ? 2 : 1; } void Action::Seek() { using Global::wHeader; using Global::wFooter; using Global::SeekingInProgress; if (!Mpd.GetTotalTime()) { ShowMessage("Unknown item length"); return; } LockProgressbar(); LockStatusbar(); int songpos = Mpd.GetElapsedTime(); time_t t = time(0); int old_timeout = wFooter->GetTimeout(); wFooter->SetTimeout(ncmpcpp_window_timeout); SeekingInProgress = true; while (true) { TraceMpdStatus(); myPlaylist->UpdateTimer(); int howmuch = Config.incremental_seeking ? (myPlaylist->Timer()-t)/2+Config.seek_time : Config.seek_time; Key input = ReadKey(*wFooter); KeyConfiguration::Binding k = Keys.Bindings.equal_range(input); // no action? if (k.first == k.second || !k.first->second.isSingle()) break; Action *a = k.first->second.getAction(); if (dynamic_cast(a)) { if (songpos < Mpd.GetTotalTime()) { songpos += howmuch; if (songpos > Mpd.GetTotalTime()) songpos = Mpd.GetTotalTime(); } } else if (dynamic_cast(a)) { if (songpos > 0) { songpos -= howmuch; if (songpos < 0) songpos = 0; } } else break; *wFooter << fmtBold; std::string tracklength; if (Config.new_design) { if (Config.display_remaining_time) { tracklength = "-"; tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()-songpos); } else tracklength = MPD::Song::ShowTime(songpos); tracklength += "/"; tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()); *wHeader << XY(0, 0) << tracklength << " "; wHeader->Refresh(); } else { tracklength = " ["; if (Config.display_remaining_time) { tracklength += "-"; tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()-songpos); } else tracklength += MPD::Song::ShowTime(songpos); tracklength += "/"; tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()); tracklength += "]"; *wFooter << XY(wFooter->GetWidth()-tracklength.length(), 1) << tracklength; } *wFooter << fmtBoldEnd; DrawProgressbar(songpos, Mpd.GetTotalTime()); wFooter->Refresh(); } SeekingInProgress = false; Mpd.Seek(songpos); wFooter->SetTimeout(old_timeout); UnlockProgressbar(); UnlockStatusbar(); } void Action::FindItem(const FindDirection fd) { using Global::wFooter; List *mList = myScreen->GetList(); if (!mList) return; LockStatusbar(); Statusbar() << "Find " << (fd == fdForward ? "forward" : "backward") << ": "; std::string findme = wFooter->GetString(); UnlockStatusbar(); if (!findme.empty()) ShowMessage("Searching..."); bool success = mList->Search(findme, 0, REG_ICASE | Config.regex_type); if (findme.empty()) return; if (success) ShowMessage("Searching finished"); else ShowMessage("Unable to find \"%s\"", findme.c_str()); if (fd == fdForward) mList->NextFound(Config.wrapped_search); else mList->PrevFound(Config.wrapped_search); if (myScreen == myPlaylist) myPlaylist->EnableHighlighting(); } void Action::ListsChangeFinisher() { if (myScreen == myLibrary || myScreen == myPlaylistEditor # ifdef HAVE_TAGLIB_H || myScreen == myTagEditor # endif // HAVE_TAGLIB_H ) { if (myScreen->ActiveWindow() == myLibrary->Artists) { myLibrary->Albums->Clear(); myLibrary->Songs->Clear(); } else if (myScreen->ActiveWindow() == myLibrary->Albums) { myLibrary->Songs->Clear(); } else if (myScreen->ActiveWindow() == myPlaylistEditor->Playlists) { myPlaylistEditor->Content->Clear(); } # ifdef HAVE_TAGLIB_H else if (myScreen->ActiveWindow() == myTagEditor->LeftColumn) { myTagEditor->Tags->Clear(); } # endif // HAVE_TAGLIB_H } } bool Action::ConnectToMPD() { if (!Mpd.Connect()) { std::cout << "Couldn't connect to MPD "; std::cout << "(host = " << Mpd.GetHostname() << ", port = " << Mpd.GetPort() << ")"; std::cout << ": " << Mpd.GetErrorMessage() << std::endl; return false; } return true; } bool Action::AskYesNoQuestion(const std::string &question, void (*callback)()) { using Global::wFooter; LockStatusbar(); Statusbar() << question << " [" << fmtBold << 'y' << fmtBoldEnd << '/' << fmtBold << 'n' << fmtBoldEnd << "]"; wFooter->Refresh(); int answer = 0; do { if (callback) callback(); answer = wFooter->ReadKey(); } while (answer != 'y' && answer != 'n'); UnlockStatusbar(); return answer == 'y'; } bool Action::isMPDMusicDirSet() { if (Config.mpd_music_dir.empty()) { ShowMessage("Proper mpd_music_dir variable has to be set in configuration file"); return false; } return true; } bool MouseEvent::canBeRun() const { return Config.mouse_support; } void MouseEvent::Run() { using Global::VolumeState; itsOldMouseEvent = itsMouseEvent; getmouse(&itsMouseEvent); // workaround shitty ncurses behavior introduced in >=5.8, when we mysteriously get // a few times after ncmpcpp startup 2^27 code instead of BUTTON{1,3}_RELEASED. since that // 2^27 thing shows constantly instead of BUTTON2_PRESSED, it was redefined to be recognized // as BUTTON2_PRESSED. but clearly we don't want to trigger behavior bound to BUTTON2 // after BUTTON{1,3} was pressed. so, here is the workaround: if last event was BUTTON{1,3}_PRESSED, // we MUST get BUTTON{1,3}_RELEASED afterwards. if we get BUTTON2_PRESSED, erroneus behavior // is about to occur and we need to prevent that. if (itsOldMouseEvent.bstate & (BUTTON1_PRESSED | BUTTON3_PRESSED) && itsMouseEvent.bstate & BUTTON2_PRESSED) return; if (itsMouseEvent.bstate & BUTTON1_PRESSED && itsMouseEvent.y == LINES-(Config.statusbar_visibility ? 2 : 1) ) // progressbar { if (!myPlaylist->isPlaying()) return; Mpd.Seek(Mpd.GetTotalTime()*itsMouseEvent.x/double(COLS)); } else if (itsMouseEvent.bstate & BUTTON1_PRESSED && (Config.statusbar_visibility || Config.new_design) && Mpd.isPlaying() && itsMouseEvent.y == (Config.new_design ? 1 : LINES-1) && itsMouseEvent.x < 9 ) // playing/paused { Mpd.Toggle(); } else if ((itsMouseEvent.bstate & BUTTON2_PRESSED || itsMouseEvent.bstate & BUTTON4_PRESSED) && Config.header_visibility && itsMouseEvent.y == 0 && size_t(itsMouseEvent.x) > COLS-VolumeState.length() ) // volume { if (itsMouseEvent.bstate & BUTTON2_PRESSED) Mpd.SetVolume(Mpd.GetVolume()-2); else Mpd.SetVolume(Mpd.GetVolume()+2); } else if (itsMouseEvent.bstate & (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED)) myScreen->MouseButtonPressed(itsMouseEvent); } void ScrollUp::Run() { myScreen->Scroll(wUp); ListsChangeFinisher(); } void ScrollDown::Run() { myScreen->Scroll(wDown); ListsChangeFinisher(); } void ScrollUpArtist::Run() { List *mList = myScreen->GetList(); if (!mList || mList->Empty()) return; size_t pos = mList->Choice(); if (MPD::Song *s = myScreen->GetSong(pos)) { std::string artist = s->getArtist(); while (pos > 0) { s = myScreen->GetSong(--pos); myScreen->Scroll(wUp); if (!s || s->getArtist() != artist) break; } } } void ScrollUpAlbum::Run() { List *mList = myScreen->GetList(); if (!mList || mList->Empty()) return; size_t pos = mList->Choice(); if (MPD::Song *s = myScreen->GetSong(pos)) { std::string album = s->getAlbum(); while (pos > 0) { s = myScreen->GetSong(--pos); myScreen->Scroll(wUp); if (!s || s->getAlbum() != album) break; } } } void ScrollDownArtist::Run() { List *mList = myScreen->GetList(); if (!mList || mList->Empty()) return; size_t pos = mList->Choice(); if (MPD::Song *s = myScreen->GetSong(pos)) { std::string artist = s->getArtist(); while (pos < mList->Size() - 1) { s = myScreen->GetSong(++pos); myScreen->Scroll(wDown); if (!s || s->getArtist() != artist) break; } } } void ScrollDownAlbum::Run() { List *mList = myScreen->GetList(); if (!mList || mList->Empty()) return; size_t pos = mList->Choice(); if (MPD::Song *s = myScreen->GetSong(pos)) { std::string album = s->getAlbum(); while (pos < mList->Size() - 1) { s = myScreen->GetSong(++pos); myScreen->Scroll(wDown); if (!s || s->getAlbum() != album) break; } } } void PageUp::Run() { myScreen->Scroll(wPageUp); ListsChangeFinisher(); } void PageDown::Run() { myScreen->Scroll(wPageDown); ListsChangeFinisher(); } void MoveHome::Run() { myScreen->Scroll(wHome); ListsChangeFinisher(); } void MoveEnd::Run() { myScreen->Scroll(wEnd); ListsChangeFinisher(); } void ToggleInterface::Run() { Config.new_design = !Config.new_design; Config.statusbar_visibility = Config.new_design ? 0 : OriginalStatusbarVisibility; SetWindowsDimensions(); UnlockProgressbar(); UnlockStatusbar(); DesignChanged = true; ResizeScreen(); } bool JumpToParentDir::canBeRun() const { return myScreen == myBrowser # ifdef HAVE_TAGLIB_H || myScreen == myTagEditor # endif // HAVE_TAGLIB_H ; } void JumpToParentDir::Run() { if (myScreen == myBrowser && myBrowser->CurrentDir() != "/") { myBrowser->Main()->Reset(); myBrowser->EnterPressed(); } # ifdef HAVE_TAGLIB_H else if (myScreen->ActiveWindow() == myTagEditor->Dirs && myTagEditor->CurrentDir() != "/") { myTagEditor->Dirs->Reset(); myTagEditor->EnterPressed(); } # endif // HAVE_TAGLIB_H } void PressEnter::Run() { myScreen->EnterPressed(); } void PressSpace::Run() { myScreen->SpacePressed(); } bool PreviousColumn::canBeRun() const { return (myScreen == myLibrary && myLibrary->isPrevColumnAvailable()) || (myScreen == myPlaylistEditor && myPlaylistEditor->isPrevColumnAvailable()) # ifdef HAVE_TAGLIB_H || (myScreen == myTagEditor && myTagEditor->isPrevColumnAvailable()) # endif // HAVE_TAGLIB_H ; } void PreviousColumn::Run() { if (myScreen == myLibrary) myLibrary->PrevColumn(); else if (myScreen == myPlaylistEditor) myPlaylistEditor->PrevColumn(); # ifdef HAVE_TAGLIB_H else if (myScreen == myTagEditor) myTagEditor->PrevColumn(); # endif // HAVE_TAGLIB_H } bool NextColumn::canBeRun() const { return (myScreen == myLibrary && myLibrary->isNextColumnAvailable()) || (myScreen == myPlaylistEditor && myPlaylistEditor->isNextColumnAvailable()) # ifdef HAVE_TAGLIB_H || (myScreen == myTagEditor && myTagEditor->isNextColumnAvailable()) # endif // HAVE_TAGLIB_H ; } void NextColumn::Run() { if (myScreen == myLibrary) myLibrary->NextColumn(); else if (myScreen == myPlaylistEditor) myPlaylistEditor->NextColumn(); # ifdef HAVE_TAGLIB_H else if (myScreen == myTagEditor) myTagEditor->NextColumn(); # endif // HAVE_TAGLIB_H } bool MasterScreen::canBeRun() const { using Global::myLockedScreen; using Global::myInactiveScreen; return myLockedScreen && myInactiveScreen && myLockedScreen != myScreen && myScreen->isMergable(); } void MasterScreen::Run() { using Global::myInactiveScreen; using Global::myLockedScreen; using Global::RedrawHeader; myInactiveScreen = myScreen; myScreen = myLockedScreen; RedrawHeader = true; } bool SlaveScreen::canBeRun() const { using Global::myLockedScreen; using Global::myInactiveScreen; return myLockedScreen && myInactiveScreen && myLockedScreen == myScreen && myScreen->isMergable(); } void SlaveScreen::Run() { using Global::myInactiveScreen; using Global::myLockedScreen; using Global::RedrawHeader; myScreen = myInactiveScreen; myInactiveScreen = myLockedScreen; RedrawHeader = true; } void VolumeUp::Run() { Mpd.SetVolume(Mpd.GetVolume()+1); } void VolumeDown::Run() { Mpd.SetVolume(Mpd.GetVolume()-1); } void Delete::Run() { using Global::wFooter; using MPD::itDirectory; using MPD::itSong; using MPD::itPlaylist; if (!myPlaylist->Items->Empty() && myScreen == myPlaylist) { if (myPlaylist->Items->hasSelected()) { std::vector list; myPlaylist->Items->GetSelected(list); Mpd.StartCommandsList(); for (std::vector::reverse_iterator it = list.rbegin(); it != list.rend(); ++it) Mpd.DeleteID((*myPlaylist->Items)[*it].value().getID()); if (Mpd.CommitCommandsList()) { for (auto it = myPlaylist->Items->Begin(); it != myPlaylist->Items->End(); ++it) it->setSelected(false); ShowMessage("Selected items deleted"); } } else Mpd.DeleteID(myPlaylist->CurrentSong()->getID()); } else if ( (myScreen == myBrowser && !myBrowser->Main()->Empty() && myBrowser->CurrentDir() == "/" && myBrowser->Main()->Current().value().type == itPlaylist) || (myScreen->ActiveWindow() == myPlaylistEditor->Playlists) ) { std::string name; if (myScreen == myBrowser) name = myBrowser->Main()->Current().value().name; else name = myPlaylistEditor->Playlists->Current().value(); bool yes = AskYesNoQuestion("Delete playlist \"" + Shorten(TO_WSTRING(name), COLS-28) + "\"?", TraceMpdStatus); if (yes) { if (Mpd.DeletePlaylist(locale_to_utf_cpy(name))) { const char msg[] = "Playlist \"%s\" deleted"; ShowMessage(msg, Shorten(TO_WSTRING(name), COLS-const_strlen(msg)).c_str()); if (myBrowser->Main() && !myBrowser->isLocal() && myBrowser->CurrentDir() == "/") myBrowser->GetDirectory("/"); } } else ShowMessage("Aborted"); if (myPlaylistEditor->Main()) // check if initialized myPlaylistEditor->Playlists->Clear(); // make playlists list update itself } # ifndef WIN32 else if (myScreen == myBrowser && !myBrowser->Main()->Empty()) { if (!myBrowser->isLocal() && !isMPDMusicDirSet()) return; MPD::Item &item = myBrowser->Main()->Current().value(); if (item.type == itSong && !Config.allow_physical_files_deletion) { ShowMessage("Deleting files is disabled by default, see man page for more details"); return; } if (item.type == itDirectory && !Config.allow_physical_directories_deletion) { ShowMessage("Deleting directories is disabled by default, see man page for more details"); return; } if (myBrowser->isParentDir(myBrowser->Main()->Choice())) return; std::string name = item.type == itSong ? item.song->getName() : item.name; std::string question; if (myBrowser->Main()->hasSelected()) question = "Delete selected items?"; else { question = "Delete "; question += (item.type == itSong ? "file" : item.type == itDirectory ? "directory" : "playlist"); question += " \""; question += Shorten(TO_WSTRING(name), COLS-30); question += "\"?"; } bool yes = AskYesNoQuestion(question, TraceMpdStatus); if (yes) { std::vector list; myBrowser->Main()->GetSelected(list); if (list.empty()) list.push_back(myBrowser->Main()->Choice()); bool success = 1; for (size_t i = 0; i < list.size(); ++i) { const MPD::Item &it = (*myBrowser->Main())[list[i]].value(); name = it.type == itSong ? it.song->getName() : it.name; if (myBrowser->DeleteItem(it)) { const char msg[] = "\"%s\" deleted"; ShowMessage(msg, Shorten(TO_WSTRING(name), COLS-const_strlen(msg)).c_str()); } else { const char msg[] = "Couldn't delete \"%s\": %s"; ShowMessage(msg, Shorten(TO_WSTRING(name), COLS-const_strlen(msg)-25).c_str(), strerror(errno)); success = 0; break; } } if (success) { if (!myBrowser->isLocal()) Mpd.UpdateDirectory(myBrowser->CurrentDir()); else myBrowser->GetDirectory(myBrowser->CurrentDir()); } } else ShowMessage("Aborted"); } # endif // !WIN32 else if (myScreen->ActiveWindow() == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty()) { if (myPlaylistEditor->Content->hasSelected()) { std::vector list; myPlaylistEditor->Content->GetSelected(list); std::string playlist = locale_to_utf_cpy(myPlaylistEditor->Playlists->Current().value()); ShowMessage("Deleting selected items..."); Mpd.StartCommandsList(); for (std::vector::reverse_iterator it = list.rbegin(); it != list.rend(); ++it) { Mpd.Delete(playlist, *it); myPlaylistEditor->Content->DeleteItem(*it); } Mpd.CommitCommandsList(); ShowMessage("Selected items deleted from playlist \"%s\"", myPlaylistEditor->Playlists->Current().value().c_str()); } else { if (Mpd.Delete(myPlaylistEditor->Playlists->Current().value(), myPlaylistEditor->Content->Choice())) myPlaylistEditor->Content->DeleteItem(myPlaylistEditor->Content->Choice()); } } } void ReplaySong::Run() { if (Mpd.isPlaying()) Mpd.Seek(0); } void PreviousSong::Run() { Mpd.Prev(); } void NextSong::Run() { Mpd.Next(); } void Pause::Run() { Mpd.Toggle(); } void SavePlaylist::Run() { using Global::wFooter; LockStatusbar(); Statusbar() << "Save playlist as: "; std::string playlist_name = wFooter->GetString(); std::string real_playlist_name = locale_to_utf_cpy(playlist_name); UnlockStatusbar(); if (playlist_name.find("/") != std::string::npos) { ShowMessage("Playlist name must not contain slashes"); return; } if (!playlist_name.empty()) { if (myPlaylist->Items->isFiltered()) { Mpd.StartCommandsList(); for (size_t i = 0; i < myPlaylist->Items->Size(); ++i) Mpd.AddToPlaylist(real_playlist_name, (*myPlaylist->Items)[i].value()); Mpd.CommitCommandsList(); if (Mpd.GetErrorMessage().empty()) ShowMessage("Filtered items added to playlist \"%s\"", playlist_name.c_str()); } else { int result = Mpd.SavePlaylist(real_playlist_name); if (result == MPD_ERROR_SUCCESS) { ShowMessage("Playlist saved as \"%s\"", playlist_name.c_str()); if (myPlaylistEditor->Main()) // check if initialized myPlaylistEditor->Playlists->Clear(); // make playlist's list update itself } else if (result == MPD_SERVER_ERROR_EXIST) { bool yes = AskYesNoQuestion("Playlist \"" + playlist_name + "\" already exists, overwrite?", TraceMpdStatus); if (yes) { Mpd.DeletePlaylist(real_playlist_name); if (Mpd.SavePlaylist(real_playlist_name) == MPD_ERROR_SUCCESS) ShowMessage("Playlist overwritten"); } else ShowMessage("Aborted"); if (myPlaylistEditor->Main()) // check if initialized myPlaylistEditor->Playlists->Clear(); // make playlist's list update itself if (myScreen == myPlaylist) myPlaylist->EnableHighlighting(); } } } if (myBrowser->Main() && !myBrowser->isLocal() && myBrowser->CurrentDir() == "/" && !myBrowser->Main()->Empty()) myBrowser->GetDirectory(myBrowser->CurrentDir()); } void Stop::Run() { Mpd.Stop(); } bool MoveSortOrderUp::canBeRun() const { return myScreen == myPlaylist && myPlaylist->SortingInProgress(); } void MoveSortOrderUp::Run() { myPlaylist->AdjustSortOrder(Playlist::mUp); } bool MoveSortOrderDown::canBeRun() const { return myScreen == myPlaylist && myPlaylist->SortingInProgress(); } void MoveSortOrderDown::Run() { myPlaylist->AdjustSortOrder(Playlist::mDown); } bool MoveSelectedItemsUp::canBeRun() const { return myScreen->ActiveWindow() == myPlaylist->Items || myScreen->ActiveWindow() == myPlaylistEditor->Content; } void MoveSelectedItemsUp::Run() { if (myScreen == myPlaylist) myPlaylist->MoveSelectedItems(Playlist::mUp); else if (myScreen == myPlaylistEditor) myPlaylistEditor->MoveSelectedItems(Playlist::mUp); } bool MoveSelectedItemsDown::canBeRun() const { return myScreen->ActiveWindow() == myPlaylist->Items || myScreen->ActiveWindow() == myPlaylistEditor->Content; } void MoveSelectedItemsDown::Run() { if (myScreen == myPlaylist) myPlaylist->MoveSelectedItems(Playlist::mDown); else if (myScreen == myPlaylistEditor) myPlaylistEditor->MoveSelectedItems(Playlist::mDown); } void MoveSelectedItemsTo::Run() { if (myPlaylist->isFiltered()) return; if (!myPlaylist->Items->hasSelected()) { ShowMessage("No selected items to move"); return; } // remove search results as we may move them to different positions, but // search rememebers positions and may point to wrong ones after that. myPlaylist->Items->Search(""); size_t pos = myPlaylist->Items->Choice(); std::vector list; myPlaylist->Items->GetSelected(list); if (pos >= list.front() && pos <= list.back()) // can't move to the middle of selected items return; ++pos; // always move after currently highlighted item int diff = pos-list.front(); Mpd.StartCommandsList(); if (diff > 0) { pos -= list.size(); size_t i = list.size()-1; std::vector::reverse_iterator it = list.rbegin(); for (; it != list.rend(); ++it, --i) Mpd.Move(*it, pos+i); if (Mpd.CommitCommandsList()) { i = list.size()-1; for (it = list.rbegin(); it != list.rend(); ++it, --i) { myPlaylist->Items->at(*it).setSelected(false); myPlaylist->Items->at(pos+i).setSelected(true); } } } else if (diff < 0) { size_t i = 0; std::vector::const_iterator it = list.begin(); for (; it != list.end(); ++it, ++i) Mpd.Move(*it, pos+i); if (Mpd.CommitCommandsList()) { i = 0; for (it = list.begin(); it != list.end(); ++it, ++i) { myPlaylist->Items->at(*it).setSelected(false); myPlaylist->Items->at(pos+i).setSelected(true); } } } } bool Add::canBeRun() const { return myScreen != myPlaylistEditor || !myPlaylistEditor->Playlists->Empty(); } void Add::Run() { using Global::wFooter; LockStatusbar(); Statusbar() << (myScreen == myPlaylistEditor ? "Add to playlist: " : "Add: "); std::string path = wFooter->GetString(); locale_to_utf(path); UnlockStatusbar(); if (!path.empty()) { Statusbar() << "Adding..."; wFooter->Refresh(); if (myScreen == myPlaylistEditor) { Mpd.AddToPlaylist(myPlaylistEditor->Playlists->Current().value(), path); myPlaylistEditor->Content->Clear(); // make it refetch content of playlist } else { const char lastfm_url[] = "lastfm://"; if (path.compare(0, const_strlen(lastfm_url), lastfm_url) == 0 || path.find(".asx", path.length()-4) != std::string::npos || path.find(".cue", path.length()-4) != std::string::npos || path.find(".m3u", path.length()-4) != std::string::npos || path.find(".pls", path.length()-4) != std::string::npos || path.find(".xspf", path.length()-5) != std::string::npos) Mpd.LoadPlaylist(path); else Mpd.Add(path); } } } bool SeekForward::canBeRun() const { return myPlaylist->NowPlayingSong(); } void SeekForward::Run() { Seek(); } bool SeekBackward::canBeRun() const { return myPlaylist->NowPlayingSong(); } void SeekBackward::Run() { Seek(); } bool ToggleDisplayMode::canBeRun() const { return myScreen == myPlaylist || myScreen == myBrowser || myScreen == mySearcher || myScreen->ActiveWindow() == myPlaylistEditor->Content; } void ToggleDisplayMode::Run() { if (myScreen == myPlaylist) { Config.columns_in_playlist = !Config.columns_in_playlist; ShowMessage("Playlist display mode: %s", Config.columns_in_playlist ? "Columns" : "Classic"); if (Config.columns_in_playlist) { myPlaylist->Items->setItemDisplayer(std::bind(Display::SongsInColumns, _1, *myPlaylist)); myPlaylist->Items->SetTitle(Config.titles_visibility ? Display::Columns(myPlaylist->Items->GetWidth()) : ""); myPlaylist->Items->SetItemStringifier(Playlist::SongInColumnsToString); } else { myPlaylist->Items->setItemDisplayer(std::bind(Display::Songs, _1, *myPlaylist, Config.song_list_format)); myPlaylist->Items->SetTitle(""); myPlaylist->Items->SetItemStringifier(Playlist::SongToString); } } else if (myScreen == myBrowser) { 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 && Config.titles_visibility ? Display::Columns(myBrowser->Main()->GetWidth()) : ""); } else if (myScreen == mySearcher) { 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 && Config.titles_visibility ? Display::Columns(mySearcher->Main()->GetWidth()) : ""); } else if (myScreen->ActiveWindow() == myPlaylistEditor->Content) { Config.columns_in_playlist_editor = !Config.columns_in_playlist_editor; ShowMessage("Playlist editor display mode: %s", Config.columns_in_playlist_editor ? "Columns" : "Classic"); if (Config.columns_in_playlist_editor) { myPlaylistEditor->Content->setItemDisplayer(std::bind(Display::SongsInColumns, _1, *myPlaylistEditor)); myPlaylistEditor->Content->SetItemStringifier(Playlist::SongInColumnsToString); } else { myPlaylistEditor->Content->setItemDisplayer(std::bind(Display::Songs, _1, *myPlaylistEditor, Config.song_list_format)); myPlaylistEditor->Content->SetItemStringifier(Playlist::SongToString); } } } bool ToggleSeparatorsInPlaylist::canBeRun() const { return myScreen == myPlaylist; } void ToggleSeparatorsInPlaylist::Run() { Config.playlist_separate_albums = !Config.playlist_separate_albums; ShowMessage("Separators between albums in playlist: %s", Config.playlist_separate_albums ? "On" : "Off"); } #ifndef HAVE_CURL_CURL_H bool ToggleLyricsFetcher::canBeRun() const { return false; } #endif // NOT HAVE_CURL_CURL_H void ToggleLyricsFetcher::Run() { # ifdef HAVE_CURL_CURL_H myLyrics->ToggleFetcher(); # endif // HAVE_CURL_CURL_H } #ifndef HAVE_CURL_CURL_H bool ToggleFetchingLyricsInBackground::canBeRun() const { return false; } #endif // NOT HAVE_CURL_CURL_H void ToggleFetchingLyricsInBackground::Run() { # ifdef HAVE_CURL_CURL_H Config.fetch_lyrics_in_background = !Config.fetch_lyrics_in_background; ShowMessage("Fetching lyrics for playing songs in background: %s", Config.fetch_lyrics_in_background ? "On" : "Off"); # endif // HAVE_CURL_CURL_H } void ToggleAutoCenter::Run() { Config.autocenter_mode = !Config.autocenter_mode; ShowMessage("Auto center mode: %s", Config.autocenter_mode ? "On" : "Off"); if (Config.autocenter_mode && myPlaylist->isPlaying() && !myPlaylist->Items->isFiltered()) myPlaylist->Items->Highlight(myPlaylist->NowPlaying); } void UpdateDatabase::Run() { if (myScreen == myBrowser) Mpd.UpdateDirectory(locale_to_utf_cpy(myBrowser->CurrentDir())); # ifdef HAVE_TAGLIB_H else if (myScreen == myTagEditor && !Config.albums_in_tag_editor) Mpd.UpdateDirectory(myTagEditor->CurrentDir()); # endif // HAVE_TAGLIB_H else Mpd.UpdateDirectory("/"); } bool JumpToPlayingSong::canBeRun() const { return (myScreen == myPlaylist || myScreen == myBrowser || myScreen == myLibrary) && myPlaylist->isPlaying(); } void JumpToPlayingSong::Run() { using Global::RedrawHeader; if (myScreen == myPlaylist) { if (myPlaylist->isFiltered()) return; assert(myPlaylist->isPlaying()); myPlaylist->Items->Highlight(myPlaylist->NowPlaying); } else if (myScreen == myBrowser) { const MPD::Song *s = myPlaylist->NowPlayingSong(); assert(s); myBrowser->LocateSong(*s); RedrawHeader = true; } else if (myScreen == myLibrary) { const MPD::Song *s = myPlaylist->NowPlayingSong(); assert(s); myLibrary->LocateSong(*s); } } void ToggleRepeat::Run() { Mpd.SetRepeat(!Mpd.GetRepeat()); } void Shuffle::Run() { Mpd.Shuffle(); } void ToggleRandom::Run() { Mpd.SetRandom(!Mpd.GetRandom()); } bool StartSearching::canBeRun() const { return myScreen == mySearcher; } void StartSearching::Run() { if (mySearcher->Main()->at(0).isInactive()) return; mySearcher->Main()->Highlight(SearchEngine::SearchButton); mySearcher->Main()->Highlighting(0); mySearcher->Main()->Refresh(); mySearcher->Main()->Highlighting(1); mySearcher->EnterPressed(); } bool SaveTagChanges::canBeRun() const { # ifdef HAVE_TAGLIB_H return myScreen == myTinyTagEditor || myScreen->ActiveWindow() == myTagEditor->TagTypes; # else return false; # endif // HAVE_TAGLIB_H } void SaveTagChanges::Run() { # ifdef HAVE_TAGLIB_H if (myScreen == myTinyTagEditor) { myTinyTagEditor->Main()->Highlight(myTinyTagEditor->Main()->Size()-2); // Save myTinyTagEditor->EnterPressed(); } else if (myScreen->ActiveWindow() == myTagEditor->TagTypes) { myTagEditor->TagTypes->Highlight(myTagEditor->TagTypes->Size()-1); // Save myTagEditor->EnterPressed(); } # endif // HAVE_TAGLIB_H } void ToggleSingle::Run() { Mpd.SetSingle(!Mpd.GetSingle()); } void ToggleConsume::Run() { Mpd.SetConsume(!Mpd.GetConsume()); } void ToggleCrossfade::Run() { Mpd.SetCrossfade(Mpd.GetCrossfade() ? 0 : Config.crossfade_time); } void SetCrossfade::Run() { using Global::wFooter; LockStatusbar(); Statusbar() << "Set crossfade to: "; std::string crossfade = wFooter->GetString(3); UnlockStatusbar(); int cf = stringToInt(crossfade); if (cf > 0) { Config.crossfade_time = cf; Mpd.SetCrossfade(cf); } } bool EditSong::canBeRun() const { # ifdef HAVE_TAGLIB_H return myScreen->CurrentSong(); # else return false; # endif // HAVE_TAGLIB_H } void EditSong::Run() { # ifdef HAVE_TAGLIB_H if (!isMPDMusicDirSet()) return; const MPD::Song *s = myScreen->CurrentSong(); assert(s); myTinyTagEditor->SetEdited(*s); myTinyTagEditor->SwitchTo(); # endif // HAVE_TAGLIB_H } bool EditLibraryTag::canBeRun() const { # ifdef HAVE_TAGLIB_H return myScreen->ActiveWindow() == myLibrary->Artists && !myLibrary->Artists->Empty(); # else return false; # endif // HAVE_TAGLIB_H } void EditLibraryTag::Run() { # ifdef HAVE_TAGLIB_H using Global::wFooter; if (!isMPDMusicDirSet()) return; LockStatusbar(); Statusbar() << fmtBold << tagTypeToString(Config.media_lib_primary_tag) << fmtBoldEnd << ": "; std::string new_tag = wFooter->GetString(myLibrary->Artists->Current().value()); UnlockStatusbar(); if (!new_tag.empty() && new_tag != myLibrary->Artists->Current().value()) { ShowMessage("Updating tags..."); Mpd.StartSearch(1); Mpd.AddSearch(Config.media_lib_primary_tag, locale_to_utf_cpy(myLibrary->Artists->Current().value())); MPD::MutableSong::SetFunction set = tagTypeToSetFunction(Config.media_lib_primary_tag); assert(set); bool success = true; std::string dir_to_update; Mpd.CommitSearchSongs([set, &new_tag, &success, &dir_to_update](MPD::Song &&s) { if (!success) return; MPD::MutableSong es = s; es.setTag(set, new_tag); ShowMessage("Updating tags in \"%s\"...", es.getName().c_str()); std::string path = Config.mpd_music_dir + es.getURI(); if (!TagEditor::WriteTags(es)) { const char msg[] = "Error while updating tags in \"%s\""; ShowMessage(msg, Shorten(TO_WSTRING(es.getURI()), COLS-const_strlen(msg)).c_str()); success = false; } if (dir_to_update.empty()) dir_to_update = es.getDirectory(); else getSharedDirectory(es.getDirectory(), dir_to_update); }); if (success) { Mpd.UpdateDirectory(dir_to_update); ShowMessage("Tags updated successfully"); } } # endif // HAVE_TAGLIB_H } bool EditLibraryAlbum::canBeRun() const { # ifdef HAVE_TAGLIB_H return myScreen->ActiveWindow() == myLibrary->Albums && !myLibrary->Albums->Empty(); # else return false; # endif // HAVE_TAGLIB_H } void EditLibraryAlbum::Run() { # ifdef HAVE_TAGLIB_H using Global::wFooter; if (!isMPDMusicDirSet()) return; LockStatusbar(); Statusbar() << fmtBold << "Album: " << fmtBoldEnd; std::string new_album = wFooter->GetString(myLibrary->Albums->Current().value().Album); UnlockStatusbar(); if (!new_album.empty() && new_album != myLibrary->Albums->Current().value().Album) { bool success = 1; ShowMessage("Updating tags..."); for (size_t i = 0; i < myLibrary->Songs->Size(); ++i) { ShowMessage("Updating tags in \"%s\"...", (*myLibrary->Songs)[i].value().getName().c_str()); std::string path = Config.mpd_music_dir + (*myLibrary->Songs)[i].value().getURI(); TagLib::FileRef f(path.c_str()); if (f.isNull()) { const char msg[] = "Error while opening file \"%s\""; ShowMessage(msg, Shorten(TO_WSTRING((*myLibrary->Songs)[i].value().getURI()), COLS-const_strlen(msg)).c_str()); success = 0; break; } f.tag()->setAlbum(ToWString(new_album)); if (!f.save()) { const char msg[] = "Error while writing tags in \"%s\""; ShowMessage(msg, Shorten(TO_WSTRING((*myLibrary->Songs)[i].value().getURI()), COLS-const_strlen(msg)).c_str()); success = 0; break; } } if (success) { Mpd.UpdateDirectory(getSharedDirectory(myLibrary->Songs)); ShowMessage("Tags updated successfully"); } } # endif // HAVE_TAGLIB_H } bool EditDirectoryName::canBeRun() const { return (myScreen == myBrowser && !myBrowser->Main()->Empty() && myBrowser->Main()->Current().value().type == MPD::itDirectory) # ifdef HAVE_TAGLIB_H || (myScreen->ActiveWindow() == myTagEditor->Dirs && !myTagEditor->Dirs->Empty() && myTagEditor->Dirs->Choice() > 0) # endif // HAVE_TAGLIB_H ; } void EditDirectoryName::Run() { using Global::wFooter; if (!isMPDMusicDirSet()) return; if (myScreen == myBrowser) { std::string old_dir = myBrowser->Main()->Current().value().name; LockStatusbar(); Statusbar() << fmtBold << "Directory: " << fmtBoldEnd; std::string new_dir = wFooter->GetString(old_dir); UnlockStatusbar(); if (!new_dir.empty() && new_dir != old_dir) { std::string full_old_dir; if (!myBrowser->isLocal()) full_old_dir += Config.mpd_music_dir; full_old_dir += locale_to_utf_cpy(old_dir); std::string full_new_dir; if (!myBrowser->isLocal()) 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) { const char msg[] = "Directory renamed to \"%s\""; ShowMessage(msg, Shorten(TO_WSTRING(new_dir), COLS-const_strlen(msg)).c_str()); if (!myBrowser->isLocal()) Mpd.UpdateDirectory(locale_to_utf_cpy(getSharedDirectory(old_dir, new_dir))); myBrowser->GetDirectory(myBrowser->CurrentDir()); } else { const char msg[] = "Couldn't rename \"%s\": %s"; ShowMessage(msg, Shorten(TO_WSTRING(old_dir), COLS-const_strlen(msg)-25).c_str(), strerror(errno)); } } } # ifdef HAVE_TAGLIB_H else if (myScreen->ActiveWindow() == myTagEditor->Dirs) { std::string old_dir = myTagEditor->Dirs->Current().value().first; LockStatusbar(); Statusbar() << fmtBold << "Directory: " << fmtBoldEnd; std::string new_dir = wFooter->GetString(old_dir); UnlockStatusbar(); if (!new_dir.empty() && new_dir != old_dir) { std::string full_old_dir = Config.mpd_music_dir + myTagEditor->CurrentDir() + "/" + locale_to_utf_cpy(old_dir); std::string full_new_dir = Config.mpd_music_dir + myTagEditor->CurrentDir() + "/" + locale_to_utf_cpy(new_dir); if (rename(full_old_dir.c_str(), full_new_dir.c_str()) == 0) { const char msg[] = "Directory renamed to \"%s\""; ShowMessage(msg, Shorten(TO_WSTRING(new_dir), COLS-const_strlen(msg)).c_str()); Mpd.UpdateDirectory(myTagEditor->CurrentDir()); } else { const char msg[] = "Couldn't rename \"%s\": %s"; ShowMessage(msg, Shorten(TO_WSTRING(old_dir), COLS-const_strlen(msg)-25).c_str(), strerror(errno)); } } } # endif // HAVE_TAGLIB_H } bool EditPlaylistName::canBeRun() const { return (myScreen->ActiveWindow() == myPlaylistEditor->Playlists && !myPlaylistEditor->Playlists->Empty()) || (myScreen == myBrowser && !myBrowser->Main()->Empty() && myBrowser->Main()->Current().value().type == MPD::itPlaylist); } void EditPlaylistName::Run() { using Global::wFooter; std::string old_name; if (myScreen->ActiveWindow() == myPlaylistEditor->Playlists) old_name = myPlaylistEditor->Playlists->Current().value(); else old_name = myBrowser->Main()->Current().value().name; LockStatusbar(); Statusbar() << fmtBold << "Playlist: " << fmtBoldEnd; std::string new_name = wFooter->GetString(old_name); UnlockStatusbar(); if (!new_name.empty() && new_name != old_name) { if (Mpd.Rename(locale_to_utf_cpy(old_name), locale_to_utf_cpy(new_name))) { const char msg[] = "Playlist renamed to \"%s\""; ShowMessage(msg, Shorten(TO_WSTRING(new_name), COLS-const_strlen(msg)).c_str()); if (myBrowser->Main() && !myBrowser->isLocal()) myBrowser->GetDirectory("/"); if (myPlaylistEditor->Main()) myPlaylistEditor->Playlists->Clear(); } } } bool EditLyrics::canBeRun() const { return myScreen == myLyrics; } void EditLyrics::Run() { myLyrics->Edit(); } bool JumpToBrowser::canBeRun() const { return myScreen->CurrentSong(); } void JumpToBrowser::Run() { MPD::Song *s = myScreen->CurrentSong(); assert(s); myBrowser->LocateSong(*s); } bool JumpToMediaLibrary::canBeRun() const { return myScreen->CurrentSong(); } void JumpToMediaLibrary::Run() { MPD::Song *s = myScreen->CurrentSong(); assert(s); myLibrary->LocateSong(*s); } bool JumpToPlaylistEditor::canBeRun() const { return myScreen == myBrowser && myBrowser->Main()->Current().value().type == MPD::itPlaylist; } void JumpToPlaylistEditor::Run() { myPlaylistEditor->Locate(myBrowser->Main()->Current().value().name); } void ToggleScreenLock::Run() { using Global::wFooter; using Global::myLockedScreen; if (myLockedScreen != 0) { BasicScreen::Unlock(); Action::SetResizeFlags(); ShowMessage("Screen unlocked"); } else { int part = Config.locked_screen_width_part*100; if (Config.ask_for_locked_screen_width_part) { LockStatusbar(); Statusbar() << "% of the locked screen's width to be reserved (20-80): "; std::string str_part = wFooter->GetString(intTo::apply(Config.locked_screen_width_part*100)); UnlockStatusbar(); if (str_part.empty()) return; part = stringToInt(str_part); } if (part < 20 || part > 80) { ShowMessage("Number is out of range"); return; } Config.locked_screen_width_part = part/100.0; if (myScreen->Lock()) ShowMessage("Screen locked (with %d%% width)", part); else ShowMessage("Current screen can't be locked"); } } bool JumpToTagEditor::canBeRun() const { # ifdef HAVE_TAGLIB_H return myScreen->CurrentSong(); # else return false; # endif // HAVE_TAGLIB_H } void JumpToTagEditor::Run() { # ifdef HAVE_TAGLIB_H if (!isMPDMusicDirSet()) return; MPD::Song *s = myScreen->CurrentSong(); assert(s); myTagEditor->LocateSong(*s); # endif // HAVE_TAGLIB_H } bool JumpToPositionInSong::canBeRun() const { return myPlaylist->NowPlayingSong(); } void JumpToPositionInSong::Run() { using Global::wFooter; if (!Mpd.GetTotalTime()) { ShowMessage("Unknown item length"); return; } const MPD::Song *s = myPlaylist->NowPlayingSong(); assert(s); LockStatusbar(); Statusbar() << "Position to go (in %/mm:ss/seconds(s)): "; std::string position = wFooter->GetString(); UnlockStatusbar(); if (position.empty()) return; int newpos = 0; if (position.find(':') != std::string::npos) // probably time in mm:ss { newpos = stringToInt(position)*60 + stringToInt(position.substr(position.find(':')+1)); if (newpos >= 0 && newpos <= Mpd.GetTotalTime()) Mpd.Seek(newpos); else ShowMessage("Out of bounds, 0:00-%s possible for mm:ss, %s given", s->getLength().c_str(), MPD::Song::ShowTime(newpos).c_str()); } else if (position.find('s') != std::string::npos) // probably position in seconds { newpos = stringToInt(position); if (newpos >= 0 && newpos <= Mpd.GetTotalTime()) Mpd.Seek(newpos); else ShowMessage("Out of bounds, 0-%d possible for seconds, %d given", s->getDuration(), newpos); } else { newpos = stringToInt(position); if (newpos >= 0 && newpos <= 100) Mpd.Seek(Mpd.GetTotalTime()*newpos/100.0); else ShowMessage("Out of bounds, 0-100 possible for %%, %d given", newpos); } } bool ReverseSelection::canBeRun() const { return myScreen->allowsSelection(); } void ReverseSelection::Run() { myScreen->ReverseSelection(); ShowMessage("Selection reversed"); } bool DeselectItems::canBeRun() const { return myScreen->allowsSelection(); } void DeselectItems::Run() { // FIXME /*List *mList = myScreen->GetList(); for (size_t i = 0; i < mList->Size(); ++i) mList->Select(i, 0); ShowMessage("Items deselected");*/ } bool SelectAlbum::canBeRun() const { return myScreen->allowsSelection() && myScreen->GetList(); } void SelectAlbum::Run() { // FIXME /*List *mList = myScreen->GetList(); assert(mList); size_t pos = mList->Choice(); if (MPD::Song *s = myScreen->GetSong(pos)) { std::string album = s->getAlbum(); // select song under cursor mList->Select(pos, 1); // go up while (pos > 0) { s = myScreen->GetSong(--pos); if (!s || s->getAlbum() != album) break; else mList->Select(pos, 1); } // go down pos = mList->Choice(); while (pos < mList->Size() - 1) { s = myScreen->GetSong(++pos); if (!s || s->getAlbum() != album) break; else mList->Select(pos, 1); } ShowMessage("Album around cursor position selected"); } */ } void AddSelectedItems::Run() { mySelectedItemsAdder->SwitchTo(); } void CropMainPlaylist::Run() { if (myPlaylist->isFiltered()) return; bool yes = true; if (Config.ask_before_clearing_main_playlist) yes = AskYesNoQuestion("Do you really want to crop main playlist?", TraceMpdStatus); if (yes) { bool delete_all_but_current = !myPlaylist->Items->hasSelected(); Mpd.StartCommandsList(); int current = myPlaylist->Items->Choice(); for (int i = myPlaylist->Items->Size()-1; i >= 0; --i) { bool delete_i = (delete_all_but_current && i != current) || (!delete_all_but_current && !myPlaylist->Items->at(i).isSelected()); if (delete_i && i != myPlaylist->NowPlaying) Mpd.Delete(i); } // 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. bool delete_np = (delete_all_but_current && current != myPlaylist->NowPlaying) || (!delete_all_but_current && !myPlaylist->Items->at(myPlaylist->NowPlaying).isSelected()); if (myPlaylist->isPlaying() && delete_np) Mpd.DeleteID(myPlaylist->NowPlayingSong()->getID()); ShowMessage("Cropping playlist..."); if (Mpd.CommitCommandsList()) ShowMessage("Playlist cropped"); } } bool CropPlaylist::canBeRun() const { return myScreen == myPlaylistEditor; } void CropPlaylist::Run() { if (myPlaylistEditor->Playlists->Empty() || myPlaylistEditor->isContentFiltered()) return; bool yes = true; if (Config.ask_before_clearing_main_playlist) yes = AskYesNoQuestion("Do you really want to crop playlist \"" + myPlaylistEditor->Playlists->Current().value() + "\"?", TraceMpdStatus); if (yes) { bool delete_all_but_current = !myPlaylistEditor->Content->hasSelected(); Mpd.StartCommandsList(); int current = myPlaylistEditor->Content->Choice(); std::string playlist = locale_to_utf_cpy(myPlaylistEditor->Playlists->Current().value()); for (int i = myPlaylistEditor->Content->Size()-1; i >= 0; --i) { bool delete_i = (delete_all_but_current && i != current) || (!delete_all_but_current && !myPlaylistEditor->Content->at(i).isSelected()); if (delete_i) Mpd.Delete(playlist, i); } ShowMessage("Cropping playlist \"%s\"...", myPlaylistEditor->Playlists->Current().value().c_str()); if (Mpd.CommitCommandsList()) { ShowMessage("Playlist \"%s\" cropped", myPlaylistEditor->Playlists->Current().value().c_str()); // enforce content update myPlaylistEditor->Content->Clear(); } } } void ClearMainPlaylist::Run() { bool yes = true; if (Config.ask_before_clearing_main_playlist) yes = AskYesNoQuestion("Do you really want to clear main playlist?", TraceMpdStatus); if (yes) { if (myPlaylist->Items->isFiltered()) { ShowMessage("Deleting filtered items..."); Mpd.StartCommandsList(); for (int i = myPlaylist->Items->Size()-1; i >= 0; --i) Mpd.Delete((*myPlaylist->Items)[i].value().getPosition()); if (Mpd.CommitCommandsList()) ShowMessage("Filtered items deleted"); } else { ShowMessage("Clearing playlist..."); if (Mpd.ClearPlaylist()) ShowMessage("Playlist cleared"); } } } bool ClearPlaylist::canBeRun() const { return myScreen == myPlaylistEditor; } void ClearPlaylist::Run() { if (myPlaylistEditor->Playlists->Empty() || myPlaylistEditor->isContentFiltered()) return; bool yes = true; if (Config.ask_before_clearing_main_playlist) yes = AskYesNoQuestion("Do you really want to clear playlist \"" + myPlaylistEditor->Playlists->Current().value() + "\"?", TraceMpdStatus); if (yes) { ShowMessage("Clearing playlist \"%s\"...", myPlaylistEditor->Playlists->Current().value().c_str()); if (Mpd.ClearPlaylist(locale_to_utf_cpy(myPlaylistEditor->Playlists->Current().value()))) ShowMessage("Playlist \"%s\" cleared", myPlaylistEditor->Playlists->Current().value().c_str()); } } bool SortPlaylist::canBeRun() const { return myScreen == myPlaylist; } void SortPlaylist::Run() { myPlaylist->Sort(); } bool ReversePlaylist::canBeRun() const { return myScreen == myPlaylist; } void ReversePlaylist::Run() { myPlaylist->Reverse(); } bool ApplyFilter::canBeRun() const { return myScreen->GetList(); } void ApplyFilter::Run() { using Global::RedrawHeader; using Global::wFooter; List *mList = myScreen->GetList(); assert(mList); LockStatusbar(); Statusbar() << fmtBold << "Apply filter: " << fmtBoldEnd; wFooter->SetGetStringHelper(StatusbarApplyFilterImmediately); wFooter->GetString(mList->GetFilter()); wFooter->SetGetStringHelper(StatusbarGetStringHelper); UnlockStatusbar(); if (mList->isFiltered()) ShowMessage("Using filter \"%s\"", mList->GetFilter().c_str()); else ShowMessage("Filtering disabled"); if (myScreen == myPlaylist) { myPlaylist->EnableHighlighting(); Playlist::ReloadTotalLength = true; RedrawHeader = true; } ListsChangeFinisher(); } void DisableFilter::Run() { using Global::wFooter; ApplyFilter *applyFilter = dynamic_cast(Get(aApplyFilter)); if (applyFilter && applyFilter->canBeRun()) { // delete current filter wFooter->PushChar(KEY_CTRL_U); wFooter->PushChar(KEY_ENTER); applyFilter->Execute(); } } bool Find::canBeRun() const { return myScreen == myHelp || myScreen == myLyrics # ifdef HAVE_CURL_CURL_H || myScreen == myLastfm # endif // HAVE_CURL_CURL_H ; } void Find::Run() { using Global::wFooter; LockStatusbar(); Statusbar() << "Find: "; std::string findme = wFooter->GetString(); UnlockStatusbar(); ShowMessage("Searching..."); Screen *s = static_cast *>(myScreen); s->Main()->RemoveFormatting(); ShowMessage("%s", findme.empty() || s->Main()->SetFormatting(fmtReverse, TO_WSTRING(findme), fmtReverseEnd, 0) ? "Done!" : "No matching patterns found"); s->Main()->Flush(); } void FindItemForward::Run() { FindItem(fdForward); ListsChangeFinisher(); } void FindItemBackward::Run() { FindItem(fdBackward); ListsChangeFinisher(); } void NextFoundItem::Run() { List *mList = myScreen->GetList(); if (!mList) return; mList->NextFound(Config.wrapped_search); ListsChangeFinisher(); } void PreviousFoundItem::Run() { List *mList = myScreen->GetList(); if (!mList) return; mList->PrevFound(Config.wrapped_search); ListsChangeFinisher(); } void ToggleFindMode::Run() { Config.wrapped_search = !Config.wrapped_search; ShowMessage("Search mode: %s", Config.wrapped_search ? "Wrapped" : "Normal"); } void ToggleReplayGainMode::Run() { using Global::wFooter; if (Mpd.Version() < 16) { ShowMessage("Replay gain mode control is supported in MPD >= 0.16.0"); return; } LockStatusbar(); Statusbar() << "Replay gain mode? [" << fmtBold << 'o' << fmtBoldEnd << "ff/" << fmtBold << 't' << fmtBoldEnd << "rack/" << fmtBold << 'a' << fmtBoldEnd << "lbum]"; wFooter->Refresh(); int answer = 0; do { TraceMpdStatus(); answer = wFooter->ReadKey(); } while (answer != 'o' && answer != 't' && answer != 'a'); UnlockStatusbar(); Mpd.SetReplayGainMode(answer == 't' ? MPD::rgmTrack : (answer == 'a' ? MPD::rgmAlbum : MPD::rgmOff)); ShowMessage("Replay gain mode: %s", Mpd.GetReplayGainMode().c_str()); } void ToggleSpaceMode::Run() { Config.space_selects = !Config.space_selects; ShowMessage("Space mode: %s item", Config.space_selects ? "Select" : "Add"); } void ToggleAddMode::Run() { 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"); } void ToggleMouse::Run() { Config.mouse_support = !Config.mouse_support; mousemask(Config.mouse_support ? ALL_MOUSE_EVENTS : 0, 0); ShowMessage("Mouse support %s", Config.mouse_support ? "enabled" : "disabled"); } void ToggleBitrateVisibility::Run() { Config.display_bitrate = !Config.display_bitrate; ShowMessage("Bitrate visibility %s", Config.display_bitrate ? "enabled" : "disabled"); } void AddRandomItems::Run() { using Global::wFooter; LockStatusbar(); Statusbar() << "Add random? [" << fmtBold << 's' << fmtBoldEnd << "ongs/" << fmtBold << 'a' << fmtBoldEnd << "rtists/al" << fmtBold << 'b' << fmtBoldEnd << "ums] "; wFooter->Refresh(); int answer = 0; do { TraceMpdStatus(); answer = wFooter->ReadKey(); } while (answer != 's' && answer != 'a' && answer != 'b'); UnlockStatusbar(); mpd_tag_type tag_type = charToTagType(answer); std::string tag_type_str = answer == 's' ? "song" : tagTypeToString(tag_type); lowercase(tag_type_str); LockStatusbar(); Statusbar() << "Number of random " << tag_type_str << "s: "; size_t number = stringToLongInt(wFooter->GetString()); UnlockStatusbar(); if (number && (answer == 's' ? Mpd.AddRandomSongs(number) : Mpd.AddRandomTag(tag_type, number))) ShowMessage("%zu random %s%s added to playlist", number, tag_type_str.c_str(), number == 1 ? "" : "s"); } bool ToggleBrowserSortMode::canBeRun() const { return myScreen == myBrowser; } void ToggleBrowserSortMode::Run() { switch (Config.browser_sort_mode) { case smName: if (!myBrowser->isLocal()) { Config.browser_sort_mode = smMTime; ShowMessage("Sort songs by: Modification time"); break; } // local browser doesn't support sorting by mtime, so we just skip it. case smMTime: Config.browser_sort_mode = smCustomFormat; ShowMessage("Sort songs by: Custom format"); break; case smCustomFormat: Config.browser_sort_mode = smName; ShowMessage("Sort songs by: Name"); break; } std::sort(myBrowser->Main()->BeginV()+(myBrowser->CurrentDir() != "/"), myBrowser->Main()->EndV(), CaseInsensitiveSorting()); } bool ToggleLibraryTagType::canBeRun() const { return (myScreen->ActiveWindow() == myLibrary->Artists) || (myLibrary->Columns() == 2 && myScreen->ActiveWindow() == myLibrary->Albums); } void ToggleLibraryTagType::Run() { using Global::wFooter; LockStatusbar(); Statusbar() << "Tag type? [" << fmtBold << 'a' << fmtBoldEnd << "rtist/album" << fmtBold << 'A' << fmtBoldEnd << "rtist/" << fmtBold << 'y' << fmtBoldEnd << "ear/" << fmtBold << 'g' << fmtBoldEnd << "enre/" << fmtBold << 'c' << fmtBoldEnd << "omposer/" << fmtBold << 'p' << fmtBoldEnd << "erformer] "; wFooter->Refresh(); int answer = 0; do { TraceMpdStatus(); answer = wFooter->ReadKey(); } while (answer != 'a' && answer != 'A' && answer != 'y' && answer != 'g' && answer != 'c' && answer != 'p'); UnlockStatusbar(); mpd_tag_type new_tagitem = charToTagType(answer); if (new_tagitem != Config.media_lib_primary_tag) { Config.media_lib_primary_tag = new_tagitem; std::string item_type = tagTypeToString(Config.media_lib_primary_tag); myLibrary->Artists->SetTitle(Config.titles_visibility ? item_type + "s" : ""); myLibrary->Artists->Reset(); lowercase(item_type); if (myLibrary->Columns() == 2) { myLibrary->Songs->Clear(); myLibrary->Albums->Reset(); myLibrary->Albums->Clear(); myLibrary->Albums->SetTitle(Config.titles_visibility ? "Albums (sorted by " + item_type + ")" : ""); myLibrary->Albums->Display(); } else { myLibrary->Artists->Clear(); myLibrary->Artists->Display(); } ShowMessage("Switched to list of %s tag", item_type.c_str()); } } bool RefetchLyrics::canBeRun() const { # ifdef HAVE_CURL_CURL_H return myScreen == myLyrics; # else return false; # endif // HAVE_CURL_CURL_H } void RefetchLyrics::Run() { # ifdef HAVE_CURL_CURL_H myLyrics->Refetch(); # endif // HAVE_CURL_CURL_H } bool RefetchArtistInfo::canBeRun() const { # ifdef HAVE_CURL_CURL_H return myScreen == myLastfm; # else return false; # endif // HAVE_CURL_CURL_H } void RefetchArtistInfo::Run() { # ifdef HAVE_CURL_CURL_H myLastfm->Refetch(); # endif // HAVE_CURL_CURL_H } bool SetSelectedItemsPriority::canBeRun() const { return myScreen->ActiveWindow() == myPlaylist->Items; } void SetSelectedItemsPriority::Run() { using Global::wFooter; assert(myScreen->ActiveWindow() == myPlaylist->Items); if (myPlaylist->Items->Empty()) return; if (Mpd.Version() < 17) { ShowMessage("Priorities are supported in MPD >= 0.17.0"); return; } LockStatusbar(); Statusbar() << "Set priority [0-255]: "; std::string strprio = wFooter->GetString(); UnlockStatusbar(); if (!isInteger(strprio.c_str())) return; int prio = atoi(strprio.c_str()); if (prio < 0 || prio > 255) { ShowMessage("Number is out of range"); return; } myPlaylist->SetSelectedItemsPriority(prio); } void ShowSongInfo::Run() { mySongInfo->SwitchTo(); } #ifndef HAVE_CURL_CURL_H bool ShowArtistInfo::canBeRun() const { return false; } #endif // NOT HAVE_CURL_CURL_H void ShowArtistInfo::Run() { # ifdef HAVE_CURL_CURL_H if (myScreen == myLastfm || myLastfm->isDownloading()) { myLastfm->SwitchTo(); return; } std::string artist; MPD::Song *s = myScreen->CurrentSong(); if (s) artist = s->getArtist(); else if (myScreen == myLibrary && myLibrary->Main() == myLibrary->Artists && !myLibrary->Artists->Empty()) artist = myLibrary->Artists->Current().value(); if (!artist.empty() && myLastfm->SetArtistInfoArgs(artist, Config.lastfm_preferred_language)) myLastfm->SwitchTo(); # endif // HAVE_CURL_CURL_H } void ShowLyrics::Run() { myLyrics->SwitchTo(); } void Quit::Run() { ExitMainLoop = true; } void NextScreen::Run() { using Global::myOldScreen; using Global::myPrevScreen; if (Config.screen_switcher_previous) { if (myScreen->isTabbable()) myPrevScreen->SwitchTo(); else myOldScreen->SwitchTo(); } else if (!Config.screens_seq.empty()) { std::list::const_iterator screen = std::find(Config.screens_seq.begin(), Config.screens_seq.end(), myScreen); if (++screen == Config.screens_seq.end()) Config.screens_seq.front()->SwitchTo(); else (*screen)->SwitchTo(); } } void PreviousScreen::Run() { using Global::myOldScreen; using Global::myPrevScreen; if (Config.screen_switcher_previous) { if (myScreen->isTabbable()) myPrevScreen->SwitchTo(); else myOldScreen->SwitchTo(); } else if (!Config.screens_seq.empty()) { std::list::const_iterator screen = std::find(Config.screens_seq.begin(), Config.screens_seq.end(), myScreen); if (screen == Config.screens_seq.begin()) Config.screens_seq.back()->SwitchTo(); else (*--screen)->SwitchTo(); } } #ifdef HAVE_TAGLIB_H bool ShowHelp::canBeRun() const { return myScreen != myTinyTagEditor; } #endif // HAVE_TAGLIB_H void ShowHelp::Run() { myHelp->SwitchTo(); } #ifdef HAVE_TAGLIB_H bool ShowPlaylist::canBeRun() const { return myScreen != myTinyTagEditor; } #endif // HAVE_TAGLIB_H void ShowPlaylist::Run() { myPlaylist->SwitchTo(); } #ifdef HAVE_TAGLIB_H bool ShowBrowser::canBeRun() const { return myScreen != myTinyTagEditor; } #endif // HAVE_TAGLIB_H void ShowBrowser::Run() { myBrowser->SwitchTo(); } #ifdef HAVE_TAGLIB_H bool ShowSearchEngine::canBeRun() const { return myScreen != myTinyTagEditor; } #endif // HAVE_TAGLIB_H void ShowSearchEngine::Run() { mySearcher->SwitchTo(); } #ifdef HAVE_TAGLIB_H bool ShowMediaLibrary::canBeRun() const { return myScreen != myTinyTagEditor; } #endif // HAVE_TAGLIB_H void ShowMediaLibrary::Run() { myLibrary->SwitchTo(); } #ifdef HAVE_TAGLIB_H bool ShowPlaylistEditor::canBeRun() const { return myScreen != myTinyTagEditor; } #endif // HAVE_TAGLIB_H void ShowPlaylistEditor::Run() { myPlaylistEditor->SwitchTo(); } bool ShowTagEditor::canBeRun() const { # ifdef HAVE_TAGLIB_H return myScreen != myTinyTagEditor; # else return false; # endif // HAVE_TAGLIB_H } void ShowTagEditor::Run() { # ifdef HAVE_TAGLIB_H if (isMPDMusicDirSet()) myTagEditor->SwitchTo(); # endif // HAVE_TAGLIB_H } bool ShowOutputs::canBeRun() const { # ifdef ENABLE_OUTPUTS # ifdef HAVE_TAGLIB_H return myScreen != myTinyTagEditor; # else return true; # endif // HAVE_TAGLIB_H # else return false; # endif // ENABLE_OUTPUTS } void ShowOutputs::Run() { # ifdef ENABLE_OUTPUTS myOutputs->SwitchTo(); # endif // ENABLE_OUTPUTS } bool ShowVisualizer::canBeRun() const { # ifdef ENABLE_OUTPUTS # ifdef HAVE_TAGLIB_H return myScreen != myTinyTagEditor; # else return true; # endif // HAVE_TAGLIB_H # else return false; # endif // ENABLE_OUTPUTS } void ShowVisualizer::Run() { # ifdef ENABLE_VISUALIZER myVisualizer->SwitchTo(); # endif // ENABLE_VISUALIZER } bool ShowClock::canBeRun() const { # ifdef ENABLE_CLOCK # ifdef HAVE_TAGLIB_H return myScreen != myTinyTagEditor; # else return true; # endif // HAVE_TAGLIB_H # else return false; # endif // ENABLE_CLOCK } void ShowClock::Run() { # ifdef ENABLE_CLOCK myClock->SwitchTo(); # endif // ENABLE_CLOCK } #ifdef HAVE_TAGLIB_H bool ShowServerInfo::canBeRun() const { return myScreen != myTinyTagEditor; } #endif // HAVE_TAGLIB_H void ShowServerInfo::Run() { myServerInfo->SwitchTo(); } Action *Action::Get(ActionType at) { if (Actions.empty()) { insertAction(new MouseEvent()); insertAction(new ScrollUp()); insertAction(new ScrollDown()); insertAction(new ScrollUpArtist()); insertAction(new ScrollUpAlbum()); insertAction(new ScrollDownArtist()); insertAction(new ScrollDownAlbum()); insertAction(new PageUp()); insertAction(new PageDown()); insertAction(new MoveHome()); insertAction(new MoveEnd()); insertAction(new ToggleInterface()); insertAction(new JumpToParentDir()); insertAction(new PressEnter()); insertAction(new PressSpace()); insertAction(new PreviousColumn()); insertAction(new NextColumn()); insertAction(new MasterScreen()); insertAction(new SlaveScreen()); insertAction(new VolumeUp()); insertAction(new VolumeDown()); insertAction(new Delete()); insertAction(new ReplaySong()); insertAction(new PreviousSong()); insertAction(new NextSong()); insertAction(new Pause()); insertAction(new Stop()); insertAction(new SavePlaylist()); insertAction(new MoveSortOrderUp()); insertAction(new MoveSortOrderDown()); insertAction(new MoveSelectedItemsUp()); insertAction(new MoveSelectedItemsDown()); insertAction(new MoveSelectedItemsTo()); insertAction(new Add()); insertAction(new SeekForward()); insertAction(new SeekBackward()); insertAction(new ToggleDisplayMode()); insertAction(new ToggleSeparatorsInPlaylist()); insertAction(new ToggleLyricsFetcher()); insertAction(new ToggleFetchingLyricsInBackground()); insertAction(new ToggleAutoCenter()); insertAction(new UpdateDatabase()); insertAction(new JumpToPlayingSong()); insertAction(new ToggleRepeat()); insertAction(new Shuffle()); insertAction(new ToggleRandom()); insertAction(new StartSearching()); insertAction(new SaveTagChanges()); insertAction(new ToggleSingle()); insertAction(new ToggleConsume()); insertAction(new ToggleCrossfade()); insertAction(new SetCrossfade()); insertAction(new EditSong()); insertAction(new EditLibraryTag()); insertAction(new EditLibraryAlbum()); insertAction(new EditDirectoryName()); insertAction(new EditPlaylistName()); insertAction(new EditLyrics()); insertAction(new JumpToBrowser()); insertAction(new JumpToMediaLibrary()); insertAction(new JumpToPlaylistEditor()); insertAction(new ToggleScreenLock()); insertAction(new JumpToTagEditor()); insertAction(new JumpToPositionInSong()); insertAction(new ReverseSelection()); insertAction(new DeselectItems()); insertAction(new SelectAlbum()); insertAction(new AddSelectedItems()); insertAction(new CropMainPlaylist()); insertAction(new CropPlaylist()); insertAction(new ClearMainPlaylist()); insertAction(new ClearPlaylist()); insertAction(new SortPlaylist()); insertAction(new ReversePlaylist()); insertAction(new ApplyFilter()); insertAction(new DisableFilter()); insertAction(new Find()); insertAction(new FindItemForward()); insertAction(new FindItemBackward()); insertAction(new NextFoundItem()); insertAction(new PreviousFoundItem()); insertAction(new ToggleFindMode()); insertAction(new ToggleReplayGainMode()); insertAction(new ToggleSpaceMode()); insertAction(new ToggleAddMode()); insertAction(new ToggleMouse()); insertAction(new ToggleBitrateVisibility()); insertAction(new AddRandomItems()); insertAction(new ToggleBrowserSortMode()); insertAction(new ToggleLibraryTagType()); insertAction(new RefetchLyrics()); insertAction(new RefetchArtistInfo()); insertAction(new SetSelectedItemsPriority()); insertAction(new ShowSongInfo()); insertAction(new ShowArtistInfo()); insertAction(new ShowLyrics()); insertAction(new Quit()); insertAction(new NextScreen()); insertAction(new PreviousScreen()); insertAction(new ShowHelp()); insertAction(new ShowPlaylist()); insertAction(new ShowBrowser()); insertAction(new ShowSearchEngine()); insertAction(new ShowMediaLibrary()); insertAction(new ShowPlaylistEditor()); insertAction(new ShowTagEditor()); insertAction(new ShowOutputs()); insertAction(new ShowVisualizer()); insertAction(new ShowClock()); insertAction(new ShowServerInfo()); } return Actions[at]; }