browser: use boost::filesystem for local browsing
[ncmpcpp/stream.git] / src / actions.cpp
bloba93dab10c0bdd172c43600680c5e69fa8c08d077
1 /***************************************************************************
2 * Copyright (C) 2008-2013 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include <cassert>
22 #include <cerrno>
23 #include <cstring>
24 #include <boost/locale/conversion.hpp>
25 #include <boost/lexical_cast.hpp>
26 #include <algorithm>
27 #include <iostream>
29 #include "actions.h"
30 #include "charset.h"
31 #include "config.h"
32 #include "display.h"
33 #include "global.h"
34 #include "mpdpp.h"
35 #include "helpers.h"
36 #include "statusbar.h"
37 #include "utility/comparators.h"
39 #include "bindings.h"
40 #include "browser.h"
41 #include "clock.h"
42 #include "help.h"
43 #include "media_library.h"
44 #include "lastfm.h"
45 #include "lyrics.h"
46 #include "playlist.h"
47 #include "playlist_editor.h"
48 #include "sort_playlist.h"
49 #include "search_engine.h"
50 #include "sel_items_adder.h"
51 #include "server_info.h"
52 #include "song_info.h"
53 #include "outputs.h"
54 #include "utility/string.h"
55 #include "utility/type_conversions.h"
56 #include "tag_editor.h"
57 #include "tiny_tag_editor.h"
58 #include "visualizer.h"
59 #include "title.h"
60 #include "tags.h"
62 #ifdef HAVE_TAGLIB_H
63 # include "fileref.h"
64 # include "tag.h"
65 #endif // HAVE_TAGLIB_H
67 using namespace std::placeholders;
68 using Global::myScreen;
70 namespace {//
72 enum class Find { Forward, Backward };
74 std::map<Actions::Type, Actions::BaseAction *> AvailableActions;
76 void populateActions();
78 void seek();
79 void findItem(const Find direction);
80 void listsChangeFinisher();
84 namespace Actions {//
86 bool OriginalStatusbarVisibility;
87 bool ExitMainLoop = false;
89 size_t HeaderHeight;
90 size_t FooterHeight;
91 size_t FooterStartY;
93 void validateScreenSize()
95 using Global::MainHeight;
97 if (COLS < 30 || MainHeight < 5)
99 NC::destroyScreen();
100 std::cout << "Screen is too small to handle ncmpcpp correctly\n";
101 exit(1);
105 void initializeScreens()
107 myHelp = new Help;
108 myPlaylist = new Playlist;
109 myBrowser = new Browser;
110 mySearcher = new SearchEngine;
111 myLibrary = new MediaLibrary;
112 myPlaylistEditor = new PlaylistEditor;
113 myLyrics = new Lyrics;
114 mySelectedItemsAdder = new SelectedItemsAdder;
115 mySongInfo = new SongInfo;
116 myServerInfo = new ServerInfo;
117 mySortPlaylistDialog = new SortPlaylistDialog;
119 # ifdef HAVE_CURL_CURL_H
120 myLastfm = new Lastfm;
121 # endif // HAVE_CURL_CURL_H
123 # ifdef HAVE_TAGLIB_H
124 myTinyTagEditor = new TinyTagEditor;
125 myTagEditor = new TagEditor;
126 # endif // HAVE_TAGLIB_H
128 # ifdef ENABLE_VISUALIZER
129 myVisualizer = new Visualizer;
130 # endif // ENABLE_VISUALIZER
132 # ifdef ENABLE_OUTPUTS
133 myOutputs = new Outputs;
134 # endif // ENABLE_OUTPUTS
136 # ifdef ENABLE_CLOCK
137 myClock = new Clock;
138 # endif // ENABLE_CLOCK
142 void setResizeFlags()
144 myHelp->hasToBeResized = 1;
145 myPlaylist->hasToBeResized = 1;
146 myBrowser->hasToBeResized = 1;
147 mySearcher->hasToBeResized = 1;
148 myLibrary->hasToBeResized = 1;
149 myPlaylistEditor->hasToBeResized = 1;
150 myLyrics->hasToBeResized = 1;
151 mySelectedItemsAdder->hasToBeResized = 1;
152 mySongInfo->hasToBeResized = 1;
153 myServerInfo->hasToBeResized = 1;
154 mySortPlaylistDialog->hasToBeResized = 1;
156 # ifdef HAVE_CURL_CURL_H
157 myLastfm->hasToBeResized = 1;
158 # endif // HAVE_CURL_CURL_H
160 # ifdef HAVE_TAGLIB_H
161 myTinyTagEditor->hasToBeResized = 1;
162 myTagEditor->hasToBeResized = 1;
163 # endif // HAVE_TAGLIB_H
165 # ifdef ENABLE_VISUALIZER
166 myVisualizer->hasToBeResized = 1;
167 # endif // ENABLE_VISUALIZER
169 # ifdef ENABLE_OUTPUTS
170 myOutputs->hasToBeResized = 1;
171 # endif // ENABLE_OUTPUTS
173 # ifdef ENABLE_CLOCK
174 myClock->hasToBeResized = 1;
175 # endif // ENABLE_CLOCK
178 void resizeScreen(bool reload_main_window)
180 using Global::MainHeight;
181 using Global::wHeader;
182 using Global::wFooter;
184 # if defined(USE_PDCURSES)
185 resize_term(0, 0);
186 # else
187 // update internal screen dimensions
188 if (reload_main_window)
190 endwin();
191 refresh();
192 // get rid of KEY_RESIZE as it sometimes
193 // corrupts our new cool ReadKey() function
194 // because KEY_RESIZE doesn't come from stdin
195 // and thus select cannot detect it
196 timeout(10);
197 getch();
198 timeout(-1);
200 # endif
202 MainHeight = LINES-(Config.new_design ? 7 : 4);
204 validateScreenSize();
206 if (!Config.header_visibility)
207 MainHeight += 2;
208 if (!Config.statusbar_visibility)
209 ++MainHeight;
211 setResizeFlags();
213 applyToVisibleWindows(&BaseScreen::resize);
215 if (Config.header_visibility || Config.new_design)
216 wHeader->resize(COLS, HeaderHeight);
218 FooterStartY = LINES-(Config.statusbar_visibility ? 2 : 1);
219 wFooter->moveTo(0, FooterStartY);
220 wFooter->resize(COLS, Config.statusbar_visibility ? 2 : 1);
222 applyToVisibleWindows(&BaseScreen::refresh);
224 Status::Changes::elapsedTime(false);
225 Status::Changes::playerState();
226 // Note: routines for drawing separator if alternative user
227 // interface is active and header is hidden are placed in
228 // NcmpcppStatusChanges.StatusFlags
229 Status::Changes::flags();
230 drawHeader();
231 wFooter->refresh();
232 refresh();
235 void setWindowsDimensions()
237 using Global::MainStartY;
238 using Global::MainHeight;
240 MainStartY = Config.new_design ? 5 : 2;
241 MainHeight = LINES-(Config.new_design ? 7 : 4);
243 if (!Config.header_visibility)
245 MainStartY -= 2;
246 MainHeight += 2;
248 if (!Config.statusbar_visibility)
249 ++MainHeight;
251 HeaderHeight = Config.new_design ? (Config.header_visibility ? 5 : 3) : 1;
252 FooterStartY = LINES-(Config.statusbar_visibility ? 2 : 1);
253 FooterHeight = Config.statusbar_visibility ? 2 : 1;
256 bool askYesNoQuestion(const std::string &question, void (*callback)())
258 using Global::wFooter;
260 Statusbar::lock();
261 Statusbar::put() << question << " [" << NC::Format::Bold << 'y' << NC::Format::NoBold << '/' << NC::Format::Bold << 'n' << NC::Format::NoBold << "]";
262 wFooter->refresh();
263 int answer = 0;
266 if (callback)
267 callback();
268 answer = wFooter->readKey();
270 while (answer != 'y' && answer != 'n');
271 Statusbar::unlock();
272 return answer == 'y';
275 bool isMPDMusicDirSet()
277 if (Config.mpd_music_dir.empty())
279 Statusbar::msg("Proper mpd_music_dir variable has to be set in configuration file");
280 return false;
282 return true;
285 BaseAction *get(Actions::Type at)
287 if (AvailableActions.empty())
288 populateActions();
289 return AvailableActions[at];
292 BaseAction *get(const std::string &name)
294 BaseAction *result = 0;
295 if (AvailableActions.empty())
296 populateActions();
297 for (auto it = AvailableActions.begin(); it != AvailableActions.end(); ++it)
299 if (it->second->name() == name)
301 result = it->second;
302 break;
305 return result;
308 bool MouseEvent::canBeRun() const
310 return Config.mouse_support;
313 void MouseEvent::run()
315 using Global::VolumeState;
317 m_old_mouse_event = m_mouse_event;
318 getmouse(&m_mouse_event);
319 // workaround shitty ncurses behavior introduced in >=5.8, when we mysteriously get
320 // a few times after ncmpcpp startup 2^27 code instead of BUTTON{1,3}_RELEASED. since that
321 // 2^27 thing shows constantly instead of BUTTON2_PRESSED, it was redefined to be recognized
322 // as BUTTON2_PRESSED. but clearly we don't want to trigger behavior bound to BUTTON2
323 // after BUTTON{1,3} was pressed. so, here is the workaround: if last event was BUTTON{1,3}_PRESSED,
324 // we MUST get BUTTON{1,3}_RELEASED afterwards. if we get BUTTON2_PRESSED, erroneus behavior
325 // is about to occur and we need to prevent that.
326 if (m_old_mouse_event.bstate & (BUTTON1_PRESSED | BUTTON3_PRESSED) && m_mouse_event.bstate & BUTTON2_PRESSED)
327 return;
328 if (m_mouse_event.bstate & BUTTON1_PRESSED
329 && m_mouse_event.y == LINES-(Config.statusbar_visibility ? 2 : 1)
330 ) // progressbar
332 if (Status::State::player() == MPD::psStop)
333 return;
334 Mpd.Seek(myPlaylist->currentSongPosition(),
335 myPlaylist->currentSongLength()*m_mouse_event.x/double(COLS));
337 else if (m_mouse_event.bstate & BUTTON1_PRESSED
338 && (Config.statusbar_visibility || Config.new_design)
339 && Status::State::player() != MPD::psStop
340 && m_mouse_event.y == (Config.new_design ? 1 : LINES-1) && m_mouse_event.x < 9
341 ) // playing/paused
343 Mpd.Toggle();
345 else if ((m_mouse_event.bstate & BUTTON2_PRESSED || m_mouse_event.bstate & BUTTON4_PRESSED)
346 && (Config.header_visibility || Config.new_design)
347 && m_mouse_event.y == 0 && size_t(m_mouse_event.x) > COLS-VolumeState.length()
348 ) // volume
350 if (m_mouse_event.bstate & BUTTON2_PRESSED)
351 get(Type::VolumeDown)->execute();
352 else
353 get(Type::VolumeUp)->execute();
355 else if (m_mouse_event.bstate & (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED))
356 myScreen->mouseButtonPressed(m_mouse_event);
359 void ScrollUp::run()
361 myScreen->scroll(NC::Scroll::Up);
362 listsChangeFinisher();
365 void ScrollDown::run()
367 myScreen->scroll(NC::Scroll::Down);
368 listsChangeFinisher();
371 bool ScrollUpArtist::canBeRun() const
373 return proxySongList(myScreen);
376 void ScrollUpArtist::run()
378 auto pl = proxySongList(myScreen);
379 assert(pl);
380 size_t pos = pl.choice();
381 if (MPD::Song *s = pl.getSong(pos))
383 std::string artist = s->getArtist();
384 while (pos > 0)
386 s = pl.getSong(--pos);
387 if (!s || s->getArtist() != artist)
388 break;
390 pl.highlight(pos);
394 bool ScrollUpAlbum::canBeRun() const
396 return proxySongList(myScreen);
399 void ScrollUpAlbum::run()
401 auto pl = proxySongList(myScreen);
402 assert(pl);
403 size_t pos = pl.choice();
404 if (MPD::Song *s = pl.getSong(pos))
406 std::string album = s->getAlbum();
407 while (pos > 0)
409 s = pl.getSong(--pos);
410 if (!s || s->getAlbum() != album)
411 break;
413 pl.highlight(pos);
417 bool ScrollDownArtist::canBeRun() const
419 return proxySongList(myScreen);
422 void ScrollDownArtist::run()
424 auto pl = proxySongList(myScreen);
425 assert(pl);
426 size_t pos = pl.choice();
427 if (MPD::Song *s = pl.getSong(pos))
429 std::string artist = s->getArtist();
430 while (pos < pl.size() - 1)
432 s = pl.getSong(++pos);
433 if (!s || s->getArtist() != artist)
434 break;
436 pl.highlight(pos);
440 bool ScrollDownAlbum::canBeRun() const
442 return proxySongList(myScreen);
445 void ScrollDownAlbum::run()
447 auto pl = proxySongList(myScreen);
448 assert(pl);
449 size_t pos = pl.choice();
450 if (MPD::Song *s = pl.getSong(pos))
452 std::string album = s->getAlbum();
453 while (pos < pl.size() - 1)
455 s = pl.getSong(++pos);
456 if (!s || s->getAlbum() != album)
457 break;
459 pl.highlight(pos);
463 void PageUp::run()
465 myScreen->scroll(NC::Scroll::PageUp);
466 listsChangeFinisher();
469 void PageDown::run()
471 myScreen->scroll(NC::Scroll::PageDown);
472 listsChangeFinisher();
475 void MoveHome::run()
477 myScreen->scroll(NC::Scroll::Home);
478 listsChangeFinisher();
481 void MoveEnd::run()
483 myScreen->scroll(NC::Scroll::End);
484 listsChangeFinisher();
487 void ToggleInterface::run()
489 Config.new_design = !Config.new_design;
490 Config.statusbar_visibility = Config.new_design ? 0 : OriginalStatusbarVisibility;
491 setWindowsDimensions();
492 Progressbar::unlock();
493 Statusbar::unlock();
494 resizeScreen(false);
495 Status::Changes::mixer();
496 Status::Changes::elapsedTime(false);
497 Statusbar::msg("User interface: %s", Config.new_design ? "Alternative" : "Classic");
500 bool JumpToParentDirectory::canBeRun() const
502 return (myScreen == myBrowser)
503 # ifdef HAVE_TAGLIB_H
504 || (myScreen->activeWindow() == myTagEditor->Dirs)
505 # endif // HAVE_TAGLIB_H
509 void JumpToParentDirectory::run()
511 if (myScreen == myBrowser)
513 if (myBrowser->CurrentDir() != "/")
515 myBrowser->main().reset();
516 myBrowser->enterPressed();
519 # ifdef HAVE_TAGLIB_H
520 else if (myScreen == myTagEditor)
522 if (myTagEditor->CurrentDir() != "/")
524 myTagEditor->Dirs->reset();
525 myTagEditor->enterPressed();
528 # endif // HAVE_TAGLIB_H
531 void PressEnter::run()
533 myScreen->enterPressed();
536 void PressSpace::run()
538 myScreen->spacePressed();
541 bool PreviousColumn::canBeRun() const
543 auto hc = hasColumns(myScreen);
544 return hc && hc->previousColumnAvailable();
547 void PreviousColumn::run()
549 hasColumns(myScreen)->previousColumn();
552 bool NextColumn::canBeRun() const
554 auto hc = hasColumns(myScreen);
555 return hc && hc->nextColumnAvailable();
558 void NextColumn::run()
560 hasColumns(myScreen)->nextColumn();
563 bool MasterScreen::canBeRun() const
565 using Global::myLockedScreen;
566 using Global::myInactiveScreen;
568 return myLockedScreen
569 && myInactiveScreen
570 && myLockedScreen != myScreen
571 && myScreen->isMergable();
574 void MasterScreen::run()
576 using Global::myInactiveScreen;
577 using Global::myLockedScreen;
579 myInactiveScreen = myScreen;
580 myScreen = myLockedScreen;
581 drawHeader();
584 bool SlaveScreen::canBeRun() const
586 using Global::myLockedScreen;
587 using Global::myInactiveScreen;
589 return myLockedScreen
590 && myInactiveScreen
591 && myLockedScreen == myScreen
592 && myScreen->isMergable();
595 void SlaveScreen::run()
597 using Global::myInactiveScreen;
598 using Global::myLockedScreen;
600 myScreen = myInactiveScreen;
601 myInactiveScreen = myLockedScreen;
602 drawHeader();
605 void VolumeUp::run()
607 int volume = std::min(Status::State::volume()+Config.volume_change_step, 100);
608 Mpd.SetVolume(volume);
611 void VolumeDown::run()
613 int volume = std::max(Status::State::volume()-Config.volume_change_step, 0);
614 Mpd.SetVolume(volume);
617 bool DeletePlaylistItems::canBeRun() const
619 return (myScreen == myPlaylist && !myPlaylist->main().empty())
620 || (myScreen->isActiveWindow(myPlaylistEditor->Content) && !myPlaylistEditor->Content.empty());
623 void DeletePlaylistItems::run()
625 if (myScreen == myPlaylist)
627 Statusbar::msg("Deleting items...");
628 auto delete_fun = std::bind(&MPD::Connection::Delete, _1, _2);
629 deleteSelectedSongs(myPlaylist->main(), delete_fun);
630 Statusbar::msg("Item(s) deleted");
632 else if (myScreen->isActiveWindow(myPlaylistEditor->Content))
634 std::string playlist = myPlaylistEditor->Playlists.current().value();
635 auto delete_fun = std::bind(&MPD::Connection::PlaylistDelete, _1, playlist, _2);
636 Statusbar::msg("Deleting items...");
637 deleteSelectedSongs(myPlaylistEditor->Content, delete_fun);
638 Statusbar::msg("Item(s) deleted");
642 bool DeleteBrowserItems::canBeRun() const
644 return myScreen == myBrowser
645 && !myBrowser->main().empty()
646 && isMPDMusicDirSet();
649 void DeleteBrowserItems::run()
651 std::string question;
652 if (hasSelected(myBrowser->main().begin(), myBrowser->main().end()))
653 question = "Delete selected items?";
654 else
656 MPD::Item &item = myBrowser->main().current().value();
657 std::string iname = item.type == MPD::itSong ? item.song->getName() : item.name;
658 question = "Delete ";
659 question += itemTypeToString(item.type);
660 question += " \"";
661 question += ToString(wideShorten(ToWString(iname), COLS-question.size()-10));
662 question += "\"?";
664 bool yes = askYesNoQuestion(question, Status::trace);
665 if (yes)
667 bool success = true;
668 auto list = getSelectedOrCurrent(myBrowser->main().begin(), myBrowser->main().end(), myBrowser->main().currentI());
669 for (auto it = list.begin(); it != list.end(); ++it)
671 const MPD::Item &i = (*it)->value();
672 std::string iname = i.type == MPD::itSong ? i.song->getName() : i.name;
673 if (myBrowser->deleteItem(i))
675 const char msg[] = "\"%ls\" deleted";
676 Statusbar::msg(msg, wideShorten(ToWString(iname), COLS-const_strlen(msg)).c_str());
678 else
680 const char msg[] = "Couldn't delete \"%ls\": %s";
681 Statusbar::msg(msg, wideShorten(ToWString(iname), COLS-const_strlen(msg)-25).c_str(), strerror(errno));
682 success = false;
683 break;
686 if (success)
688 if (myBrowser->isLocal())
689 myBrowser->GetDirectory(myBrowser->CurrentDir());
690 else
691 Mpd.UpdateDirectory(myBrowser->CurrentDir());
694 else
695 Statusbar::msg("Aborted");
698 bool DeleteStoredPlaylist::canBeRun() const
700 return myScreen->isActiveWindow(myPlaylistEditor->Playlists)
701 && myPlaylistEditor->Playlists.empty();
704 void DeleteStoredPlaylist::run()
706 std::string question;
707 if (hasSelected(myPlaylistEditor->Playlists.begin(), myPlaylistEditor->Playlists.end()))
708 question = "Delete selected playlists?";
709 else
711 question = "Delete playlist \"";
712 question += ToString(wideShorten(ToWString(myPlaylistEditor->Playlists.current().value()), COLS-question.size()-10));
713 question += "\"?";
715 bool yes = askYesNoQuestion(question, Status::trace);
716 if (yes)
718 auto list = getSelectedOrCurrent(myPlaylistEditor->Playlists.begin(), myPlaylistEditor->Playlists.end(), myPlaylistEditor->Playlists.currentI());
719 Mpd.StartCommandsList();
720 for (auto it = list.begin(); it != list.end(); ++it)
721 Mpd.DeletePlaylist((*it)->value());
722 Mpd.CommitCommandsList();
723 Statusbar::msg("Playlist%s deleted", list.size() == 1 ? "" : "s");
725 else
726 Statusbar::msg("Aborted");
729 void ReplaySong::run()
731 if (Status::State::player() != MPD::psStop)
732 Mpd.Seek(myPlaylist->currentSongPosition(), 0);
735 void PreviousSong::run()
737 Mpd.Prev();
740 void NextSong::run()
742 Mpd.Next();
745 void Pause::run()
747 Mpd.Toggle();
750 void SavePlaylist::run()
752 using Global::wFooter;
754 Statusbar::lock();
755 Statusbar::put() << "Save playlist as: ";
756 std::string playlist_name = wFooter->getString();
757 Statusbar::unlock();
758 if (playlist_name.find("/") != std::string::npos)
760 Statusbar::msg("Playlist name must not contain slashes");
761 return;
763 if (!playlist_name.empty())
765 if (myPlaylist->main().isFiltered())
767 Mpd.StartCommandsList();
768 for (size_t i = 0; i < myPlaylist->main().size(); ++i)
769 Mpd.AddToPlaylist(playlist_name, myPlaylist->main()[i].value());
770 Mpd.CommitCommandsList();
771 Statusbar::msg("Filtered items added to playlist \"%s\"", playlist_name.c_str());
773 else
777 Mpd.SavePlaylist(playlist_name);
778 Statusbar::msg("Playlist saved as \"%s\"", playlist_name.c_str());
780 catch (MPD::ServerError &e)
782 if (e.code() == MPD_SERVER_ERROR_EXIST)
784 bool yes = askYesNoQuestion("Playlist \"" + playlist_name + "\" already exists, overwrite?", Status::trace);
785 if (yes)
787 Mpd.DeletePlaylist(playlist_name);
788 Mpd.SavePlaylist(playlist_name);
789 Statusbar::msg("Playlist overwritten");
791 else
792 Statusbar::msg("Aborted");
793 if (myScreen == myPlaylist)
794 myPlaylist->EnableHighlighting();
796 else
797 throw e;
801 if (!myBrowser->isLocal()
802 && myBrowser->CurrentDir() == "/"
803 && !myBrowser->main().empty())
804 myBrowser->GetDirectory(myBrowser->CurrentDir());
807 void Stop::run()
809 Mpd.Stop();
812 void ExecuteCommand::run()
814 using Global::wFooter;
815 Statusbar::lock();
816 Statusbar::put() << NC::Format::Bold << ":" << NC::Format::NoBold;
817 wFooter->setGetStringHelper(Statusbar::Helpers::TryExecuteImmediateCommand());
818 std::string cmd_name = wFooter->getString();
819 wFooter->setGetStringHelper(Statusbar::Helpers::getString);
820 Statusbar::unlock();
821 if (cmd_name.empty())
822 return;
823 auto cmd = Bindings.findCommand(cmd_name);
824 if (cmd)
826 Statusbar::msg(1, "Executing %s...", cmd_name.c_str());
827 cmd->binding().execute();
829 else
830 Statusbar::msg("No command named \"%s\"", cmd_name.c_str());
833 bool MoveSortOrderUp::canBeRun() const
835 return myScreen == mySortPlaylistDialog;
838 void MoveSortOrderUp::run()
840 mySortPlaylistDialog->moveSortOrderUp();
843 bool MoveSortOrderDown::canBeRun() const
845 return myScreen == mySortPlaylistDialog;
848 void MoveSortOrderDown::run()
850 mySortPlaylistDialog->moveSortOrderDown();
853 bool MoveSelectedItemsUp::canBeRun() const
855 return ((myScreen == myPlaylist
856 && !myPlaylist->main().empty()
857 && !myPlaylist->isFiltered())
858 || (myScreen->isActiveWindow(myPlaylistEditor->Content)
859 && !myPlaylistEditor->Content.empty()
860 && !myPlaylistEditor->isContentFiltered()));
863 void MoveSelectedItemsUp::run()
865 if (myScreen == myPlaylist)
867 moveSelectedItemsUp(myPlaylist->main(), std::bind(&MPD::Connection::Move, _1, _2, _3));
869 else if (myScreen == myPlaylistEditor)
871 assert(!myPlaylistEditor->Playlists.empty());
872 std::string playlist = myPlaylistEditor->Playlists.current().value();
873 auto move_fun = std::bind(&MPD::Connection::PlaylistMove, _1, playlist, _2, _3);
874 moveSelectedItemsUp(myPlaylistEditor->Content, move_fun);
878 bool MoveSelectedItemsDown::canBeRun() const
880 return ((myScreen == myPlaylist
881 && !myPlaylist->main().empty()
882 && !myPlaylist->isFiltered())
883 || (myScreen->isActiveWindow(myPlaylistEditor->Content)
884 && !myPlaylistEditor->Content.empty()
885 && !myPlaylistEditor->isContentFiltered()));
888 void MoveSelectedItemsDown::run()
890 if (myScreen == myPlaylist)
892 moveSelectedItemsDown(myPlaylist->main(), std::bind(&MPD::Connection::Move, _1, _2, _3));
894 else if (myScreen == myPlaylistEditor)
896 assert(!myPlaylistEditor->Playlists.empty());
897 std::string playlist = myPlaylistEditor->Playlists.current().value();
898 auto move_fun = std::bind(&MPD::Connection::PlaylistMove, _1, playlist, _2, _3);
899 moveSelectedItemsDown(myPlaylistEditor->Content, move_fun);
903 bool MoveSelectedItemsTo::canBeRun() const
905 return myScreen == myPlaylist
906 || myScreen->isActiveWindow(myPlaylistEditor->Content);
909 void MoveSelectedItemsTo::run()
911 if (myScreen == myPlaylist)
912 moveSelectedItemsTo(myPlaylist->main(), std::bind(&MPD::Connection::Move, _1, _2, _3));
913 else
915 assert(!myPlaylistEditor->Playlists.empty());
916 std::string playlist = myPlaylistEditor->Playlists.current().value();
917 auto move_fun = std::bind(&MPD::Connection::PlaylistMove, _1, playlist, _2, _3);
918 moveSelectedItemsTo(myPlaylistEditor->Content, move_fun);
922 bool Add::canBeRun() const
924 return myScreen != myPlaylistEditor
925 || !myPlaylistEditor->Playlists.empty();
928 void Add::run()
930 using Global::wFooter;
932 Statusbar::lock();
933 Statusbar::put() << (myScreen == myPlaylistEditor ? "Add to playlist: " : "Add: ");
934 std::string path = wFooter->getString();
935 Statusbar::unlock();
936 if (!path.empty())
938 Statusbar::put() << "Adding...";
939 wFooter->refresh();
940 if (myScreen == myPlaylistEditor)
941 Mpd.AddToPlaylist(myPlaylistEditor->Playlists.current().value(), path);
942 else
944 const char lastfm_url[] = "lastfm://";
945 if (path.compare(0, const_strlen(lastfm_url), lastfm_url) == 0
946 || path.find(".asx", path.length()-4) != std::string::npos
947 || path.find(".cue", path.length()-4) != std::string::npos
948 || path.find(".m3u", path.length()-4) != std::string::npos
949 || path.find(".pls", path.length()-4) != std::string::npos
950 || path.find(".xspf", path.length()-5) != std::string::npos)
951 Mpd.LoadPlaylist(path);
952 else
953 Mpd.Add(path);
958 bool SeekForward::canBeRun() const
960 return Status::State::player() != MPD::psStop && myPlaylist->currentSongLength() > 0;
963 void SeekForward::run()
965 seek();
968 bool SeekBackward::canBeRun() const
970 return Status::State::player() != MPD::psStop && myPlaylist->currentSongLength() > 0;
973 void SeekBackward::run()
975 seek();
978 bool ToggleDisplayMode::canBeRun() const
980 return myScreen == myPlaylist
981 || myScreen == myBrowser
982 || myScreen == mySearcher
983 || myScreen->isActiveWindow(myPlaylistEditor->Content);
986 void ToggleDisplayMode::run()
988 if (myScreen == myPlaylist)
990 Config.columns_in_playlist = !Config.columns_in_playlist;
991 Statusbar::msg("Playlist display mode: %s", Config.columns_in_playlist ? "Columns" : "Classic");
993 if (Config.columns_in_playlist)
995 myPlaylist->main().setItemDisplayer(std::bind(Display::SongsInColumns, _1, myPlaylist->proxySongList()));
996 if (Config.titles_visibility)
997 myPlaylist->main().setTitle(Display::Columns(myPlaylist->main().getWidth()));
998 else
999 myPlaylist->main().setTitle("");
1001 else
1003 myPlaylist->main().setItemDisplayer(std::bind(Display::Songs, _1, myPlaylist->proxySongList(), Config.song_list_format));
1004 myPlaylist->main().setTitle("");
1007 else if (myScreen == myBrowser)
1009 Config.columns_in_browser = !Config.columns_in_browser;
1010 Statusbar::msg("Browser display mode: %s", Config.columns_in_browser ? "Columns" : "Classic");
1011 myBrowser->main().setTitle(Config.columns_in_browser && Config.titles_visibility ? Display::Columns(myBrowser->main().getWidth()) : "");
1013 else if (myScreen == mySearcher)
1015 Config.columns_in_search_engine = !Config.columns_in_search_engine;
1016 Statusbar::msg("Search engine display mode: %s", Config.columns_in_search_engine ? "Columns" : "Classic");
1017 if (mySearcher->main().size() > SearchEngine::StaticOptions)
1018 mySearcher->main().setTitle(Config.columns_in_search_engine && Config.titles_visibility ? Display::Columns(mySearcher->main().getWidth()) : "");
1020 else if (myScreen->isActiveWindow(myPlaylistEditor->Content))
1022 Config.columns_in_playlist_editor = !Config.columns_in_playlist_editor;
1023 Statusbar::msg("Playlist editor display mode: %s", Config.columns_in_playlist_editor ? "Columns" : "Classic");
1024 if (Config.columns_in_playlist_editor)
1025 myPlaylistEditor->Content.setItemDisplayer(std::bind(Display::SongsInColumns, _1, myPlaylistEditor->contentProxyList()));
1026 else
1027 myPlaylistEditor->Content.setItemDisplayer(std::bind(Display::Songs, _1, myPlaylistEditor->contentProxyList(), Config.song_list_format));
1031 bool ToggleSeparatorsBetweenAlbums::canBeRun() const
1033 return true;
1036 void ToggleSeparatorsBetweenAlbums::run()
1038 Config.playlist_separate_albums = !Config.playlist_separate_albums;
1039 Statusbar::msg("Separators between albums: %s", Config.playlist_separate_albums ? "On" : "Off");
1042 #ifndef HAVE_CURL_CURL_H
1043 bool ToggleLyricsFetcher::canBeRun() const
1045 return false;
1047 #endif // NOT HAVE_CURL_CURL_H
1049 void ToggleLyricsFetcher::run()
1051 # ifdef HAVE_CURL_CURL_H
1052 myLyrics->ToggleFetcher();
1053 # endif // HAVE_CURL_CURL_H
1056 #ifndef HAVE_CURL_CURL_H
1057 bool ToggleFetchingLyricsInBackground::canBeRun() const
1059 return false;
1061 #endif // NOT HAVE_CURL_CURL_H
1063 void ToggleFetchingLyricsInBackground::run()
1065 # ifdef HAVE_CURL_CURL_H
1066 Config.fetch_lyrics_in_background = !Config.fetch_lyrics_in_background;
1067 Statusbar::msg("Fetching lyrics for playing songs in background: %s", Config.fetch_lyrics_in_background ? "On" : "Off");
1068 # endif // HAVE_CURL_CURL_H
1071 void TogglePlayingSongCentering::run()
1073 Config.autocenter_mode = !Config.autocenter_mode;
1074 Statusbar::msg("Centering playing song: %s", Config.autocenter_mode ? "On" : "Off");
1075 if (Config.autocenter_mode
1076 && Status::State::player() != MPD::psStop
1077 && !myPlaylist->main().isFiltered())
1078 myPlaylist->main().highlight(myPlaylist->currentSongPosition());
1081 void UpdateDatabase::run()
1083 if (myScreen == myBrowser)
1084 Mpd.UpdateDirectory(myBrowser->CurrentDir());
1085 # ifdef HAVE_TAGLIB_H
1086 else if (myScreen == myTagEditor)
1087 Mpd.UpdateDirectory(myTagEditor->CurrentDir());
1088 # endif // HAVE_TAGLIB_H
1089 else
1090 Mpd.UpdateDirectory("/");
1093 bool JumpToPlayingSong::canBeRun() const
1095 return ((myScreen == myPlaylist && !myPlaylist->isFiltered())
1096 || myScreen == myBrowser
1097 || myScreen == myLibrary)
1098 && Status::State::player() != MPD::psStop;
1101 void JumpToPlayingSong::run()
1103 if (myScreen == myPlaylist)
1104 myPlaylist->main().highlight(myPlaylist->currentSongPosition());
1105 else if (myScreen == myBrowser)
1107 myBrowser->LocateSong(myPlaylist->nowPlayingSong());
1108 drawHeader();
1110 else if (myScreen == myLibrary)
1112 myLibrary->LocateSong(myPlaylist->nowPlayingSong());
1116 void ToggleRepeat::run()
1118 Mpd.SetRepeat(!Status::State::repeat());
1121 void Shuffle::run()
1123 Mpd.Shuffle();
1126 void ToggleRandom::run()
1128 Mpd.SetRandom(!Status::State::random());
1131 bool StartSearching::canBeRun() const
1133 return myScreen == mySearcher && !mySearcher->main()[0].isInactive();
1136 void StartSearching::run()
1138 mySearcher->main().highlight(SearchEngine::SearchButton);
1139 mySearcher->main().setHighlighting(0);
1140 mySearcher->main().refresh();
1141 mySearcher->main().setHighlighting(1);
1142 mySearcher->enterPressed();
1145 bool SaveTagChanges::canBeRun() const
1147 # ifdef HAVE_TAGLIB_H
1148 return myScreen == myTinyTagEditor
1149 || myScreen->activeWindow() == myTagEditor->TagTypes;
1150 # else
1151 return false;
1152 # endif // HAVE_TAGLIB_H
1155 void SaveTagChanges::run()
1157 # ifdef HAVE_TAGLIB_H
1158 if (myScreen == myTinyTagEditor)
1160 myTinyTagEditor->main().highlight(myTinyTagEditor->main().size()-2); // Save
1161 myTinyTagEditor->enterPressed();
1163 else if (myScreen->activeWindow() == myTagEditor->TagTypes)
1165 myTagEditor->TagTypes->highlight(myTagEditor->TagTypes->size()-1); // Save
1166 myTagEditor->enterPressed();
1168 # endif // HAVE_TAGLIB_H
1171 void ToggleSingle::run()
1173 Mpd.SetSingle(!Status::State::single());
1176 void ToggleConsume::run()
1178 Mpd.SetConsume(!Status::State::consume());
1181 void ToggleCrossfade::run()
1183 Mpd.SetCrossfade(Status::State::crossfade() ? 0 : Config.crossfade_time);
1186 void SetCrossfade::run()
1188 using Global::wFooter;
1190 Statusbar::lock();
1191 Statusbar::put() << "Set crossfade to: ";
1192 std::string crossfade = wFooter->getString(3);
1193 Statusbar::unlock();
1194 int cf = boost::lexical_cast<int>(crossfade);
1195 if (cf > 0)
1197 Config.crossfade_time = cf;
1198 Mpd.SetCrossfade(cf);
1202 void SetVolume::run()
1204 using Global::wFooter;
1206 Statusbar::lock();
1207 Statusbar::put() << "Set volume to: ";
1208 std::string strvolume = wFooter->getString(3);
1209 Statusbar::unlock();
1210 int volume = boost::lexical_cast<int>(strvolume);
1211 if (volume >= 0 && volume <= 100)
1213 Mpd.SetVolume(volume);
1214 Statusbar::msg("Volume set to %d%%", volume);
1218 bool EditSong::canBeRun() const
1220 # ifdef HAVE_TAGLIB_H
1221 return currentSong(myScreen)
1222 && isMPDMusicDirSet();
1223 # else
1224 return false;
1225 # endif // HAVE_TAGLIB_H
1228 void EditSong::run()
1230 # ifdef HAVE_TAGLIB_H
1231 auto s = currentSong(myScreen);
1232 myTinyTagEditor->SetEdited(*s);
1233 myTinyTagEditor->switchTo();
1234 # endif // HAVE_TAGLIB_H
1237 bool EditLibraryTag::canBeRun() const
1239 # ifdef HAVE_TAGLIB_H
1240 return myScreen->isActiveWindow(myLibrary->Tags)
1241 && !myLibrary->Tags.empty()
1242 && isMPDMusicDirSet();
1243 # else
1244 return false;
1245 # endif // HAVE_TAGLIB_H
1248 void EditLibraryTag::run()
1250 # ifdef HAVE_TAGLIB_H
1251 using Global::wFooter;
1253 Statusbar::lock();
1254 Statusbar::put() << NC::Format::Bold << tagTypeToString(Config.media_lib_primary_tag) << NC::Format::NoBold << ": ";
1255 std::string new_tag = wFooter->getString(myLibrary->Tags.current().value().tag());
1256 Statusbar::unlock();
1257 if (!new_tag.empty() && new_tag != myLibrary->Tags.current().value().tag())
1259 Statusbar::msg("Updating tags...");
1260 Mpd.StartSearch(1);
1261 Mpd.AddSearch(Config.media_lib_primary_tag, myLibrary->Tags.current().value().tag());
1262 MPD::MutableSong::SetFunction set = tagTypeToSetFunction(Config.media_lib_primary_tag);
1263 assert(set);
1264 bool success = true;
1265 std::string dir_to_update;
1266 Mpd.CommitSearchSongs([set, &dir_to_update, &new_tag, &success](MPD::Song &&s) {
1267 if (!success)
1268 return;
1269 MPD::MutableSong ms = s;
1270 ms.setTags(set, new_tag, Config.tags_separator);
1271 Statusbar::msg("Updating tags in \"%s\"...", ms.getName().c_str());
1272 std::string path = Config.mpd_music_dir + ms.getURI();
1273 if (!Tags::write(ms))
1275 const char msg[] = "Error while updating tags in \"%ls\"";
1276 Statusbar::msg(msg, wideShorten(ToWString(ms.getURI()), COLS-const_strlen(msg)).c_str());
1277 success = false;
1279 if (dir_to_update.empty())
1280 dir_to_update = s.getURI();
1281 else
1282 dir_to_update = getSharedDirectory(dir_to_update, s.getURI());
1284 if (success)
1286 Mpd.UpdateDirectory(dir_to_update);
1287 Statusbar::msg("Tags updated successfully");
1290 # endif // HAVE_TAGLIB_H
1293 bool EditLibraryAlbum::canBeRun() const
1295 # ifdef HAVE_TAGLIB_H
1296 return myScreen->isActiveWindow(myLibrary->Albums)
1297 && !myLibrary->Albums.empty()
1298 && isMPDMusicDirSet();
1299 # else
1300 return false;
1301 # endif // HAVE_TAGLIB_H
1304 void EditLibraryAlbum::run()
1306 # ifdef HAVE_TAGLIB_H
1307 using Global::wFooter;
1309 Statusbar::lock();
1310 Statusbar::put() << NC::Format::Bold << "Album: " << NC::Format::NoBold;
1311 std::string new_album = wFooter->getString(myLibrary->Albums.current().value().entry().album());
1312 Statusbar::unlock();
1313 if (!new_album.empty() && new_album != myLibrary->Albums.current().value().entry().album())
1315 bool success = 1;
1316 Statusbar::msg("Updating tags...");
1317 for (size_t i = 0; i < myLibrary->Songs.size(); ++i)
1319 Statusbar::msg("Updating tags in \"%s\"...", myLibrary->Songs[i].value().getName().c_str());
1320 std::string path = Config.mpd_music_dir + myLibrary->Songs[i].value().getURI();
1321 TagLib::FileRef f(path.c_str());
1322 if (f.isNull())
1324 const char msg[] = "Error while opening file \"%ls\"";
1325 Statusbar::msg(msg, wideShorten(ToWString(myLibrary->Songs[i].value().getURI()), COLS-const_strlen(msg)).c_str());
1326 success = 0;
1327 break;
1329 f.tag()->setAlbum(ToWString(new_album));
1330 if (!f.save())
1332 const char msg[] = "Error while writing tags in \"%ls\"";
1333 Statusbar::msg(msg, wideShorten(ToWString(myLibrary->Songs[i].value().getURI()), COLS-const_strlen(msg)).c_str());
1334 success = 0;
1335 break;
1338 if (success)
1340 Mpd.UpdateDirectory(getSharedDirectory(myLibrary->Songs.beginV(), myLibrary->Songs.endV()));
1341 Statusbar::msg("Tags updated successfully");
1344 # endif // HAVE_TAGLIB_H
1347 bool EditDirectoryName::canBeRun() const
1349 return ((myScreen == myBrowser
1350 && !myBrowser->main().empty()
1351 && myBrowser->main().current().value().type == MPD::itDirectory)
1352 # ifdef HAVE_TAGLIB_H
1353 || (myScreen->activeWindow() == myTagEditor->Dirs
1354 && !myTagEditor->Dirs->empty()
1355 && myTagEditor->Dirs->choice() > 0)
1356 # endif // HAVE_TAGLIB_H
1357 ) && isMPDMusicDirSet();
1360 void EditDirectoryName::run()
1362 using Global::wFooter;
1364 if (myScreen == myBrowser)
1366 std::string old_dir = myBrowser->main().current().value().name;
1367 Statusbar::lock();
1368 Statusbar::put() << NC::Format::Bold << "Directory: " << NC::Format::NoBold;
1369 std::string new_dir = wFooter->getString(old_dir);
1370 Statusbar::unlock();
1371 if (!new_dir.empty() && new_dir != old_dir)
1373 std::string full_old_dir;
1374 if (!myBrowser->isLocal())
1375 full_old_dir += Config.mpd_music_dir;
1376 full_old_dir += old_dir;
1377 std::string full_new_dir;
1378 if (!myBrowser->isLocal())
1379 full_new_dir += Config.mpd_music_dir;
1380 full_new_dir += new_dir;
1381 int rename_result = rename(full_old_dir.c_str(), full_new_dir.c_str());
1382 if (rename_result == 0)
1384 const char msg[] = "Directory renamed to \"%ls\"";
1385 Statusbar::msg(msg, wideShorten(ToWString(new_dir), COLS-const_strlen(msg)).c_str());
1386 if (!myBrowser->isLocal())
1387 Mpd.UpdateDirectory(getSharedDirectory(old_dir, new_dir));
1388 myBrowser->GetDirectory(myBrowser->CurrentDir());
1390 else
1392 const char msg[] = "Couldn't rename \"%ls\": %s";
1393 Statusbar::msg(msg, wideShorten(ToWString(old_dir), COLS-const_strlen(msg)-25).c_str(), strerror(errno));
1397 # ifdef HAVE_TAGLIB_H
1398 else if (myScreen->activeWindow() == myTagEditor->Dirs)
1400 std::string old_dir = myTagEditor->Dirs->current().value().first;
1401 Statusbar::lock();
1402 Statusbar::put() << NC::Format::Bold << "Directory: " << NC::Format::NoBold;
1403 std::string new_dir = wFooter->getString(old_dir);
1404 Statusbar::unlock();
1405 if (!new_dir.empty() && new_dir != old_dir)
1407 std::string full_old_dir = Config.mpd_music_dir + myTagEditor->CurrentDir() + "/" + old_dir;
1408 std::string full_new_dir = Config.mpd_music_dir + myTagEditor->CurrentDir() + "/" + new_dir;
1409 if (rename(full_old_dir.c_str(), full_new_dir.c_str()) == 0)
1411 const char msg[] = "Directory renamed to \"%ls\"";
1412 Statusbar::msg(msg, wideShorten(ToWString(new_dir), COLS-const_strlen(msg)).c_str());
1413 Mpd.UpdateDirectory(myTagEditor->CurrentDir());
1415 else
1417 const char msg[] = "Couldn't rename \"%ls\": %s";
1418 Statusbar::msg(msg, wideShorten(ToWString(old_dir), COLS-const_strlen(msg)-25).c_str(), strerror(errno));
1422 # endif // HAVE_TAGLIB_H
1425 bool EditPlaylistName::canBeRun() const
1427 return (myScreen->isActiveWindow(myPlaylistEditor->Playlists)
1428 && !myPlaylistEditor->Playlists.empty())
1429 || (myScreen == myBrowser
1430 && !myBrowser->main().empty()
1431 && myBrowser->main().current().value().type == MPD::itPlaylist);
1434 void EditPlaylistName::run()
1436 using Global::wFooter;
1438 std::string old_name;
1439 if (myScreen->isActiveWindow(myPlaylistEditor->Playlists))
1440 old_name = myPlaylistEditor->Playlists.current().value();
1441 else
1442 old_name = myBrowser->main().current().value().name;
1443 Statusbar::lock();
1444 Statusbar::put() << NC::Format::Bold << "Playlist: " << NC::Format::NoBold;
1445 std::string new_name = wFooter->getString(old_name);
1446 Statusbar::unlock();
1447 if (!new_name.empty() && new_name != old_name)
1449 Mpd.Rename(old_name, new_name);
1450 const char msg[] = "Playlist renamed to \"%ls\"";
1451 Statusbar::msg(msg, wideShorten(ToWString(new_name), COLS-const_strlen(msg)).c_str());
1452 if (!myBrowser->isLocal())
1453 myBrowser->GetDirectory("/");
1457 bool EditLyrics::canBeRun() const
1459 return myScreen == myLyrics;
1462 void EditLyrics::run()
1464 myLyrics->Edit();
1467 bool JumpToBrowser::canBeRun() const
1469 return currentSong(myScreen);
1472 void JumpToBrowser::run()
1474 auto s = currentSong(myScreen);
1475 myBrowser->LocateSong(*s);
1478 bool JumpToMediaLibrary::canBeRun() const
1480 return currentSong(myScreen);
1483 void JumpToMediaLibrary::run()
1485 auto s = currentSong(myScreen);
1486 myLibrary->LocateSong(*s);
1489 bool JumpToPlaylistEditor::canBeRun() const
1491 return myScreen == myBrowser
1492 && myBrowser->main().current().value().type == MPD::itPlaylist;
1495 void JumpToPlaylistEditor::run()
1497 myPlaylistEditor->Locate(myBrowser->main().current().value().name);
1500 void ToggleScreenLock::run()
1502 using Global::wFooter;
1503 using Global::myLockedScreen;
1505 if (myLockedScreen != 0)
1507 BaseScreen::unlock();
1508 Actions::setResizeFlags();
1509 myScreen->resize();
1510 Statusbar::msg("Screen unlocked");
1512 else
1514 int part = Config.locked_screen_width_part*100;
1515 if (Config.ask_for_locked_screen_width_part)
1517 Statusbar::lock();
1518 Statusbar::put() << "% of the locked screen's width to be reserved (20-80): ";
1519 std::string str_part = wFooter->getString(boost::lexical_cast<std::string>(Config.locked_screen_width_part*100));
1520 Statusbar::unlock();
1521 if (str_part.empty())
1522 return;
1523 part = boost::lexical_cast<int>(str_part);
1525 if (part < 20 || part > 80)
1527 Statusbar::msg("Number is out of range");
1528 return;
1530 Config.locked_screen_width_part = part/100.0;
1531 if (myScreen->lock())
1532 Statusbar::msg("Screen locked (with %d%% width)", part);
1533 else
1534 Statusbar::msg("Current screen can't be locked");
1538 bool JumpToTagEditor::canBeRun() const
1540 # ifdef HAVE_TAGLIB_H
1541 return currentSong(myScreen)
1542 && isMPDMusicDirSet();
1543 # else
1544 return false;
1545 # endif // HAVE_TAGLIB_H
1548 void JumpToTagEditor::run()
1550 # ifdef HAVE_TAGLIB_H
1551 auto s = currentSong(myScreen);
1552 myTagEditor->LocateSong(*s);
1553 # endif // HAVE_TAGLIB_H
1556 bool JumpToPositionInSong::canBeRun() const
1558 return Status::State::player() != MPD::psStop && myPlaylist->currentSongLength() > 0;
1561 void JumpToPositionInSong::run()
1563 using Global::wFooter;
1565 const MPD::Song s = myPlaylist->nowPlayingSong();
1567 Statusbar::lock();
1568 Statusbar::put() << "Position to go (in %/mm:ss/seconds(s)): ";
1569 std::string position = wFooter->getString();
1570 Statusbar::unlock();
1572 if (position.empty())
1573 return;
1575 unsigned newpos = 0;
1576 if (position.find(':') != std::string::npos) // probably time in mm:ss
1578 newpos = boost::lexical_cast<int>(position)*60
1579 + boost::lexical_cast<int>(position.substr(position.find(':')+1));
1580 if (newpos <= myPlaylist->currentSongLength())
1581 Mpd.Seek(s.getPosition(), newpos);
1582 else
1583 Statusbar::msg("Out of bounds, 0:00-%s possible for mm:ss, %s given", s.getLength().c_str(), MPD::Song::ShowTime(newpos).c_str());
1585 else if (position.find('s') != std::string::npos) // probably position in seconds
1587 newpos = boost::lexical_cast<int>(position);
1588 if (newpos <= s.getDuration())
1589 Mpd.Seek(s.getPosition(), newpos);
1590 else
1591 Statusbar::msg("Out of bounds, 0-%d possible for seconds, %d given", s.getDuration(), newpos);
1593 else
1595 newpos = boost::lexical_cast<int>(position);
1596 if (newpos <= 100)
1597 Mpd.Seek(s.getPosition(), s.getDuration()*newpos/100.0);
1598 else
1599 Statusbar::msg("Out of bounds, 0-100 possible for %%, %d given", newpos);
1603 bool ReverseSelection::canBeRun() const
1605 auto w = hasSongs(myScreen);
1606 return w && w->allowsSelection();
1609 void ReverseSelection::run()
1611 auto w = hasSongs(myScreen);
1612 w->reverseSelection();
1613 Statusbar::msg("Selection reversed");
1616 bool RemoveSelection::canBeRun() const
1618 return proxySongList(myScreen);
1621 void RemoveSelection::run()
1623 auto pl = proxySongList(myScreen);
1624 for (size_t i = 0; i < pl.size(); ++i)
1625 pl.setSelected(i, false);
1626 Statusbar::msg("Selection removed");
1629 bool SelectAlbum::canBeRun() const
1631 auto w = hasSongs(myScreen);
1632 return w && w->allowsSelection() && w->proxySongList();
1635 void SelectAlbum::run()
1637 auto pl = proxySongList(myScreen);
1638 size_t pos = pl.choice();
1639 if (MPD::Song *s = pl.getSong(pos))
1641 std::string album = s->getAlbum();
1642 // select song under cursor
1643 pl.setSelected(pos, true);
1644 // go up
1645 while (pos > 0)
1647 s = pl.getSong(--pos);
1648 if (!s || s->getAlbum() != album)
1649 break;
1650 else
1651 pl.setSelected(pos, true);
1653 // go down
1654 pos = pl.choice();
1655 while (pos < pl.size() - 1)
1657 s = pl.getSong(++pos);
1658 if (!s || s->getAlbum() != album)
1659 break;
1660 else
1661 pl.setSelected(pos, true);
1663 Statusbar::msg("Album around cursor position selected");
1667 bool AddSelectedItems::canBeRun() const
1669 return myScreen != mySelectedItemsAdder;
1672 void AddSelectedItems::run()
1674 mySelectedItemsAdder->switchTo();
1677 void CropMainPlaylist::run()
1679 bool yes = true;
1680 if (Config.ask_before_clearing_main_playlist)
1681 yes = askYesNoQuestion("Do you really want to crop main playlist?", Status::trace);
1682 if (yes)
1684 Statusbar::msg("Cropping playlist...");
1685 cropPlaylist(myPlaylist->main(), std::bind(&MPD::Connection::Delete, _1, _2));
1686 Statusbar::msg("Cropping playlist...");
1690 bool CropPlaylist::canBeRun() const
1692 return myScreen == myPlaylistEditor;
1695 void CropPlaylist::run()
1697 assert(!myPlaylistEditor->Playlists.empty());
1698 std::string playlist = myPlaylistEditor->Playlists.current().value();
1699 bool yes = true;
1700 if (Config.ask_before_clearing_main_playlist)
1701 yes = askYesNoQuestion("Do you really want to crop playlist \"" + playlist + "\"?", Status::trace);
1702 if (yes)
1704 auto delete_fun = std::bind(&MPD::Connection::PlaylistDelete, _1, playlist, _2);
1705 Statusbar::msg("Cropping playlist \"%s\"...", playlist.c_str());
1706 cropPlaylist(myPlaylistEditor->Content, delete_fun);
1707 Statusbar::msg("Playlist \"%s\" cropped", playlist.c_str());
1711 void ClearMainPlaylist::run()
1713 bool yes = true;
1714 if (Config.ask_before_clearing_main_playlist)
1715 yes = askYesNoQuestion("Do you really want to clear main playlist?", Status::trace);
1716 if (yes)
1718 auto delete_fun = std::bind(&MPD::Connection::Delete, _1, _2);
1719 auto clear_fun = std::bind(&MPD::Connection::ClearMainPlaylist, _1);
1720 Statusbar::msg("Deleting items...");
1721 clearPlaylist(myPlaylist->main(), delete_fun, clear_fun);
1722 Statusbar::msg("Items deleted");
1726 bool ClearPlaylist::canBeRun() const
1728 return myScreen == myPlaylistEditor;
1731 void ClearPlaylist::run()
1733 assert(!myPlaylistEditor->Playlists.empty());
1734 std::string playlist = myPlaylistEditor->Playlists.current().value();
1735 bool yes = true;
1736 if (Config.ask_before_clearing_main_playlist)
1737 yes = askYesNoQuestion("Do you really want to clear playlist \"" + playlist + "\"?", Status::trace);
1738 if (yes)
1740 auto delete_fun = std::bind(&MPD::Connection::PlaylistDelete, _1, playlist, _2);
1741 auto clear_fun = std::bind(&MPD::Connection::ClearPlaylist, _1, playlist);
1742 Statusbar::msg("Deleting items from \"%s\"...", playlist.c_str());
1743 clearPlaylist(myPlaylistEditor->Content, delete_fun, clear_fun);
1744 Statusbar::msg("Items deleted from \"%s\"", playlist.c_str());
1748 bool SortPlaylist::canBeRun() const
1750 return myScreen == myPlaylist;
1753 void SortPlaylist::run()
1755 mySortPlaylistDialog->switchTo();
1758 bool ReversePlaylist::canBeRun() const
1760 return myScreen == myPlaylist;
1763 void ReversePlaylist::run()
1765 myPlaylist->Reverse();
1768 bool ApplyFilter::canBeRun() const
1770 auto w = dynamic_cast<Filterable *>(myScreen);
1771 return w && w->allowsFiltering();
1774 void ApplyFilter::run()
1776 using Global::wFooter;
1778 Filterable *f = dynamic_cast<Filterable *>(myScreen);
1779 std::string filter = f->currentFilter();
1781 Statusbar::lock();
1782 Statusbar::put() << NC::Format::Bold << "Apply filter: " << NC::Format::NoBold;
1783 wFooter->setGetStringHelper(Statusbar::Helpers::ApplyFilterImmediately(f, ToWString(filter)));
1784 wFooter->getString(filter);
1785 wFooter->setGetStringHelper(Statusbar::Helpers::getString);
1786 Statusbar::unlock();
1788 filter = f->currentFilter();
1789 if (filter.empty())
1791 myPlaylist->main().clearFilterResults();
1792 Statusbar::msg("Filtering disabled");
1794 else
1796 // apply filter here so even if old one wasn't modified
1797 // (and callback wasn't invoked), it still gets applied.
1798 f->applyFilter(filter);
1799 Statusbar::msg("Using filter \"%s\"", filter.c_str());
1802 if (myScreen == myPlaylist)
1804 myPlaylist->EnableHighlighting();
1805 Playlist::ReloadTotalLength = true;
1806 drawHeader();
1808 listsChangeFinisher();
1811 bool Find::canBeRun() const
1813 return myScreen == myHelp
1814 || myScreen == myLyrics
1815 # ifdef HAVE_CURL_CURL_H
1816 || myScreen == myLastfm
1817 # endif // HAVE_CURL_CURL_H
1821 void Find::run()
1823 using Global::wFooter;
1825 Statusbar::lock();
1826 Statusbar::put() << "Find: ";
1827 std::string findme = wFooter->getString();
1828 Statusbar::unlock();
1830 Statusbar::msg("Searching...");
1831 auto s = static_cast<Screen<NC::Scrollpad> *>(myScreen);
1832 s->main().removeProperties();
1833 Statusbar::msg("%s", findme.empty() || s->main().setProperties(NC::Format::Reverse, findme, NC::Format::NoReverse) ? "Done" : "No matching patterns found");
1834 s->main().flush();
1837 bool FindItemBackward::canBeRun() const
1839 auto w = dynamic_cast<Searchable *>(myScreen);
1840 return w && w->allowsSearching();
1843 void FindItemForward::run()
1845 findItem(::Find::Forward);
1846 listsChangeFinisher();
1849 bool FindItemForward::canBeRun() const
1851 auto w = dynamic_cast<Searchable *>(myScreen);
1852 return w && w->allowsSearching();
1855 void FindItemBackward::run()
1857 findItem(::Find::Backward);
1858 listsChangeFinisher();
1861 bool NextFoundItem::canBeRun() const
1863 return dynamic_cast<Searchable *>(myScreen);
1866 void NextFoundItem::run()
1868 Searchable *w = dynamic_cast<Searchable *>(myScreen);
1869 w->nextFound(Config.wrapped_search);
1870 listsChangeFinisher();
1873 bool PreviousFoundItem::canBeRun() const
1875 return dynamic_cast<Searchable *>(myScreen);
1878 void PreviousFoundItem::run()
1880 Searchable *w = dynamic_cast<Searchable *>(myScreen);
1881 w->prevFound(Config.wrapped_search);
1882 listsChangeFinisher();
1885 void ToggleFindMode::run()
1887 Config.wrapped_search = !Config.wrapped_search;
1888 Statusbar::msg("Search mode: %s", Config.wrapped_search ? "Wrapped" : "Normal");
1891 void ToggleReplayGainMode::run()
1893 using Global::wFooter;
1895 Statusbar::lock();
1896 Statusbar::put() << "Replay gain mode? [" << NC::Format::Bold << 'o' << NC::Format::NoBold << "ff/" << NC::Format::Bold << 't' << NC::Format::NoBold << "rack/" << NC::Format::Bold << 'a' << NC::Format::NoBold << "lbum]";
1897 wFooter->refresh();
1898 int answer = 0;
1901 Status::trace();
1902 answer = wFooter->readKey();
1904 while (answer != 'o' && answer != 't' && answer != 'a');
1905 Statusbar::unlock();
1906 Mpd.SetReplayGainMode(answer == 't' ? MPD::rgmTrack : (answer == 'a' ? MPD::rgmAlbum : MPD::rgmOff));
1907 Statusbar::msg("Replay gain mode: %s", Mpd.GetReplayGainMode().c_str());
1910 void ToggleSpaceMode::run()
1912 Config.space_selects = !Config.space_selects;
1913 Statusbar::msg("Space mode: %s item", Config.space_selects ? "Select" : "Add");
1916 void ToggleAddMode::run()
1918 Config.ncmpc_like_songs_adding = !Config.ncmpc_like_songs_adding;
1919 Statusbar::msg("Add mode: %s", Config.ncmpc_like_songs_adding ? "Add item to playlist, remove if already added" : "Always add item to playlist");
1922 void ToggleMouse::run()
1924 Config.mouse_support = !Config.mouse_support;
1925 mousemask(Config.mouse_support ? ALL_MOUSE_EVENTS : 0, 0);
1926 Statusbar::msg("Mouse support %s", Config.mouse_support ? "enabled" : "disabled");
1929 void ToggleBitrateVisibility::run()
1931 Config.display_bitrate = !Config.display_bitrate;
1932 Statusbar::msg("Bitrate visibility %s", Config.display_bitrate ? "enabled" : "disabled");
1935 void AddRandomItems::run()
1937 using Global::wFooter;
1939 Statusbar::lock();
1940 Statusbar::put() << "Add random? [" << NC::Format::Bold << 's' << NC::Format::NoBold << "ongs/" << NC::Format::Bold << 'a' << NC::Format::NoBold << "rtists/al" << NC::Format::Bold << 'b' << NC::Format::NoBold << "ums] ";
1941 wFooter->refresh();
1942 int answer = 0;
1945 Status::trace();
1946 answer = wFooter->readKey();
1948 while (answer != 's' && answer != 'a' && answer != 'b');
1949 Statusbar::unlock();
1951 mpd_tag_type tag_type = MPD_TAG_ARTIST;
1952 std::string tag_type_str ;
1953 if (answer != 's')
1955 tag_type = charToTagType(answer);
1956 tag_type_str = boost::locale::to_lower(tagTypeToString(tag_type));
1958 else
1959 tag_type_str = "song";
1961 Statusbar::lock();
1962 Statusbar::put() << "Number of random " << tag_type_str << "s: ";
1963 size_t number = boost::lexical_cast<size_t>(wFooter->getString());
1964 Statusbar::unlock();
1965 if (number && (answer == 's' ? Mpd.AddRandomSongs(number) : Mpd.AddRandomTag(tag_type, number)))
1966 Statusbar::msg("%zu random %s%s added to playlist", number, tag_type_str.c_str(), number == 1 ? "" : "s");
1969 bool ToggleBrowserSortMode::canBeRun() const
1971 return myScreen == myBrowser;
1974 void ToggleBrowserSortMode::run()
1976 switch (Config.browser_sort_mode)
1978 case smName:
1979 Config.browser_sort_mode = smMTime;
1980 Statusbar::msg("Sort songs by: Modification time");
1981 break;
1982 case smMTime:
1983 Config.browser_sort_mode = smCustomFormat;
1984 Statusbar::msg("Sort songs by: Custom format");
1985 break;
1986 case smCustomFormat:
1987 Config.browser_sort_mode = smName;
1988 Statusbar::msg("Sort songs by: Name");
1989 break;
1991 std::sort(myBrowser->main().begin()+(myBrowser->CurrentDir() != "/"), myBrowser->main().end(),
1992 LocaleBasedItemSorting(std::locale(), Config.ignore_leading_the, Config.browser_sort_mode));
1995 bool ToggleLibraryTagType::canBeRun() const
1997 return (myScreen->isActiveWindow(myLibrary->Tags))
1998 || (myLibrary->Columns() == 2 && myScreen->isActiveWindow(myLibrary->Albums));
2001 void ToggleLibraryTagType::run()
2003 using Global::wFooter;
2005 Statusbar::lock();
2006 Statusbar::put() << "Tag type? [" << NC::Format::Bold << 'a' << NC::Format::NoBold << "rtist/album" << NC::Format::Bold << 'A' << NC::Format::NoBold << "rtist/" << NC::Format::Bold << 'y' << NC::Format::NoBold << "ear/" << NC::Format::Bold << 'g' << NC::Format::NoBold << "enre/" << NC::Format::Bold << 'c' << NC::Format::NoBold << "omposer/" << NC::Format::Bold << 'p' << NC::Format::NoBold << "erformer] ";
2007 wFooter->refresh();
2008 int answer = 0;
2011 Status::trace();
2012 answer = wFooter->readKey();
2014 while (answer != 'a' && answer != 'A' && answer != 'y' && answer != 'g' && answer != 'c' && answer != 'p');
2015 Statusbar::unlock();
2016 mpd_tag_type new_tagitem = charToTagType(answer);
2017 if (new_tagitem != Config.media_lib_primary_tag)
2019 Config.media_lib_primary_tag = new_tagitem;
2020 std::string item_type = tagTypeToString(Config.media_lib_primary_tag);
2021 myLibrary->Tags.setTitle(Config.titles_visibility ? item_type + "s" : "");
2022 myLibrary->Tags.reset();
2023 item_type = boost::locale::to_lower(item_type);
2024 std::string and_mtime = Config.media_library_sort_by_mtime ?
2025 " and mtime" :
2027 if (myLibrary->Columns() == 2)
2029 myLibrary->Songs.clear();
2030 myLibrary->Albums.reset();
2031 myLibrary->Albums.clear();
2032 myLibrary->Albums.setTitle(Config.titles_visibility ? "Albums (sorted by " + item_type + and_mtime + ")" : "");
2033 myLibrary->Albums.display();
2035 else
2037 myLibrary->Tags.clear();
2038 myLibrary->Tags.display();
2040 Statusbar::msg("Switched to list of %s tag", item_type.c_str());
2044 bool ToggleMediaLibrarySortMode::canBeRun() const
2046 return myScreen == myLibrary;
2049 void ToggleMediaLibrarySortMode::run()
2051 myLibrary->toggleSortMode();
2054 bool RefetchLyrics::canBeRun() const
2056 # ifdef HAVE_CURL_CURL_H
2057 return myScreen == myLyrics;
2058 # else
2059 return false;
2060 # endif // HAVE_CURL_CURL_H
2063 void RefetchLyrics::run()
2065 # ifdef HAVE_CURL_CURL_H
2066 myLyrics->Refetch();
2067 # endif // HAVE_CURL_CURL_H
2070 bool RefetchArtistInfo::canBeRun() const
2072 # ifdef HAVE_CURL_CURL_H
2073 return myScreen == myLastfm;
2074 # else
2075 return false;
2076 # endif // HAVE_CURL_CURL_H
2079 void RefetchArtistInfo::run()
2081 # ifdef HAVE_CURL_CURL_H
2082 myLastfm->Refetch();
2083 # endif // HAVE_CURL_CURL_H
2086 bool SetSelectedItemsPriority::canBeRun() const
2088 if (Mpd.Version() < 17)
2090 Statusbar::msg("Priorities are supported in MPD >= 0.17.0");
2091 return false;
2093 return myScreen == myPlaylist && !myPlaylist->main().empty();
2096 void SetSelectedItemsPriority::run()
2098 using Global::wFooter;
2100 Statusbar::lock();
2101 Statusbar::put() << "Set priority [0-255]: ";
2102 std::string strprio = wFooter->getString();
2103 Statusbar::unlock();
2104 if (!isInteger(strprio.c_str(), true))
2105 return;
2106 int prio = boost::lexical_cast<int>(strprio);
2107 if (prio < 0 || prio > 255)
2109 Statusbar::msg("Number is out of range");
2110 return;
2112 myPlaylist->SetSelectedItemsPriority(prio);
2115 bool FilterPlaylistOnPriorities::canBeRun() const
2117 return myScreen == myPlaylist;
2120 void FilterPlaylistOnPriorities::run()
2122 using Global::wFooter;
2124 Statusbar::lock();
2125 Statusbar::put() << "Show songs with priority higher than: ";
2126 std::string strprio = wFooter->getString();
2127 Statusbar::unlock();
2128 if (!isInteger(strprio.c_str(), false))
2129 return;
2130 unsigned prio = boost::lexical_cast<unsigned>(strprio);
2131 myPlaylist->main().filter(myPlaylist->main().begin(), myPlaylist->main().end(),
2132 [prio](const NC::Menu<MPD::Song>::Item &s) {
2133 return s.value().getPrio() > prio;
2135 Statusbar::msg("Playlist filtered (songs with priority higher than %u)", prio);
2138 void ShowSongInfo::run()
2140 mySongInfo->switchTo();
2143 bool ShowArtistInfo::canBeRun() const
2145 #ifdef HAVE_CURL_CURL_H
2146 return myScreen == myLastfm
2147 || (myScreen->isActiveWindow(myLibrary->Tags)
2148 && !myLibrary->Tags.empty()
2149 && Config.media_lib_primary_tag == MPD_TAG_ARTIST)
2150 || currentSong(myScreen);
2151 # else
2152 return false;
2153 # endif // NOT HAVE_CURL_CURL_H
2156 void ShowArtistInfo::run()
2158 # ifdef HAVE_CURL_CURL_H
2159 if (myScreen == myLastfm || myLastfm->isDownloading())
2161 myLastfm->switchTo();
2162 return;
2165 std::string artist;
2166 if (myScreen->isActiveWindow(myLibrary->Tags))
2168 assert(!myLibrary->Tags.empty());
2169 assert(Config.media_lib_primary_tag == MPD_TAG_ARTIST);
2170 artist = myLibrary->Tags.current().value().tag();
2172 else
2174 auto s = currentSong(myScreen);
2175 assert(s);
2176 artist = s->getArtist();
2179 if (!artist.empty() && myLastfm->SetArtistInfoArgs(artist, Config.lastfm_preferred_language))
2180 myLastfm->switchTo();
2181 # endif // HAVE_CURL_CURL_H
2184 void ShowLyrics::run()
2186 myLyrics->switchTo();
2189 void Quit::run()
2191 ExitMainLoop = true;
2194 void NextScreen::run()
2196 if (Config.screen_switcher_previous)
2198 if (auto tababble = dynamic_cast<Tabbable *>(myScreen))
2199 tababble->switchToPreviousScreen();
2201 else if (!Config.screens_seq.empty())
2203 auto screen = std::find(Config.screens_seq.begin(), Config.screens_seq.end(), myScreen);
2204 if (++screen == Config.screens_seq.end())
2205 Config.screens_seq.front()->switchTo();
2206 else
2207 (*screen)->switchTo();
2211 void PreviousScreen::run()
2213 if (Config.screen_switcher_previous)
2215 if (auto tababble = dynamic_cast<Tabbable *>(myScreen))
2216 tababble->switchToPreviousScreen();
2218 else if (!Config.screens_seq.empty())
2220 auto screen = std::find(Config.screens_seq.begin(), Config.screens_seq.end(), myScreen);
2221 if (screen == Config.screens_seq.begin())
2222 Config.screens_seq.back()->switchTo();
2223 else
2224 (*--screen)->switchTo();
2228 bool ShowHelp::canBeRun() const
2230 return myScreen != myHelp
2231 # ifdef HAVE_TAGLIB_H
2232 && myScreen != myTinyTagEditor
2233 # endif // HAVE_TAGLIB_H
2237 void ShowHelp::run()
2239 myHelp->switchTo();
2242 bool ShowPlaylist::canBeRun() const
2244 return myScreen != myPlaylist
2245 # ifdef HAVE_TAGLIB_H
2246 && myScreen != myTinyTagEditor
2247 # endif // HAVE_TAGLIB_H
2251 void ShowPlaylist::run()
2253 myPlaylist->switchTo();
2256 bool ShowBrowser::canBeRun() const
2258 return myScreen != myBrowser
2259 # ifdef HAVE_TAGLIB_H
2260 && myScreen != myTinyTagEditor
2261 # endif // HAVE_TAGLIB_H
2265 void ShowBrowser::run()
2267 myBrowser->switchTo();
2270 bool ChangeBrowseMode::canBeRun() const
2272 return myScreen == myBrowser;
2275 void ChangeBrowseMode::run()
2277 myBrowser->ChangeBrowseMode();
2280 bool ShowSearchEngine::canBeRun() const
2282 return myScreen != mySearcher
2283 # ifdef HAVE_TAGLIB_H
2284 && myScreen != myTinyTagEditor
2285 # endif // HAVE_TAGLIB_H
2289 void ShowSearchEngine::run()
2291 mySearcher->switchTo();
2294 bool ResetSearchEngine::canBeRun() const
2296 return myScreen == mySearcher;
2299 void ResetSearchEngine::run()
2301 mySearcher->reset();
2304 bool ShowMediaLibrary::canBeRun() const
2306 return myScreen != myLibrary
2307 # ifdef HAVE_TAGLIB_H
2308 && myScreen != myTinyTagEditor
2309 # endif // HAVE_TAGLIB_H
2313 void ShowMediaLibrary::run()
2315 myLibrary->switchTo();
2318 bool ToggleMediaLibraryColumnsMode::canBeRun() const
2320 return myScreen == myLibrary;
2323 void ToggleMediaLibraryColumnsMode::run()
2325 myLibrary->toggleColumnsMode();
2326 myLibrary->refresh();
2329 bool ShowPlaylistEditor::canBeRun() const
2331 return myScreen != myPlaylistEditor
2332 # ifdef HAVE_TAGLIB_H
2333 && myScreen != myTinyTagEditor
2334 # endif // HAVE_TAGLIB_H
2338 void ShowPlaylistEditor::run()
2340 myPlaylistEditor->switchTo();
2343 bool ShowTagEditor::canBeRun() const
2345 # ifdef HAVE_TAGLIB_H
2346 return myScreen != myTagEditor
2347 && myScreen != myTinyTagEditor;
2348 # else
2349 return false;
2350 # endif // HAVE_TAGLIB_H
2353 void ShowTagEditor::run()
2355 # ifdef HAVE_TAGLIB_H
2356 if (isMPDMusicDirSet())
2357 myTagEditor->switchTo();
2358 # endif // HAVE_TAGLIB_H
2361 bool ShowOutputs::canBeRun() const
2363 # ifdef ENABLE_OUTPUTS
2364 return myScreen != myOutputs
2365 # ifdef HAVE_TAGLIB_H
2366 && myScreen != myTinyTagEditor
2367 # endif // HAVE_TAGLIB_H
2369 # else
2370 return false;
2371 # endif // ENABLE_OUTPUTS
2374 void ShowOutputs::run()
2376 # ifdef ENABLE_OUTPUTS
2377 myOutputs->switchTo();
2378 # endif // ENABLE_OUTPUTS
2381 bool ShowVisualizer::canBeRun() const
2383 # ifdef ENABLE_VISUALIZER
2384 return myScreen != myVisualizer
2385 # ifdef HAVE_TAGLIB_H
2386 && myScreen != myTinyTagEditor
2387 # endif // HAVE_TAGLIB_H
2389 # else
2390 return false;
2391 # endif // ENABLE_VISUALIZER
2394 void ShowVisualizer::run()
2396 # ifdef ENABLE_VISUALIZER
2397 myVisualizer->switchTo();
2398 # endif // ENABLE_VISUALIZER
2401 bool ShowClock::canBeRun() const
2403 # ifdef ENABLE_CLOCK
2404 return myScreen != myClock
2405 # ifdef HAVE_TAGLIB_H
2406 && myScreen != myTinyTagEditor
2407 # endif // HAVE_TAGLIB_H
2409 # else
2410 return false;
2411 # endif // ENABLE_CLOCK
2414 void ShowClock::run()
2416 # ifdef ENABLE_CLOCK
2417 myClock->switchTo();
2418 # endif // ENABLE_CLOCK
2421 #ifdef HAVE_TAGLIB_H
2422 bool ShowServerInfo::canBeRun() const
2424 return myScreen != myTinyTagEditor;
2426 #endif // HAVE_TAGLIB_H
2428 void ShowServerInfo::run()
2430 myServerInfo->switchTo();
2435 namespace {//
2437 void populateActions()
2439 auto insert_action = [](Actions::BaseAction *a) {
2440 AvailableActions[a->type()] = a;
2442 insert_action(new Actions::Dummy());
2443 insert_action(new Actions::MouseEvent());
2444 insert_action(new Actions::ScrollUp());
2445 insert_action(new Actions::ScrollDown());
2446 insert_action(new Actions::ScrollUpArtist());
2447 insert_action(new Actions::ScrollUpAlbum());
2448 insert_action(new Actions::ScrollDownArtist());
2449 insert_action(new Actions::ScrollDownAlbum());
2450 insert_action(new Actions::PageUp());
2451 insert_action(new Actions::PageDown());
2452 insert_action(new Actions::MoveHome());
2453 insert_action(new Actions::MoveEnd());
2454 insert_action(new Actions::ToggleInterface());
2455 insert_action(new Actions::JumpToParentDirectory());
2456 insert_action(new Actions::PressEnter());
2457 insert_action(new Actions::PressSpace());
2458 insert_action(new Actions::PreviousColumn());
2459 insert_action(new Actions::NextColumn());
2460 insert_action(new Actions::MasterScreen());
2461 insert_action(new Actions::SlaveScreen());
2462 insert_action(new Actions::VolumeUp());
2463 insert_action(new Actions::VolumeDown());
2464 insert_action(new Actions::DeletePlaylistItems());
2465 insert_action(new Actions::DeleteStoredPlaylist());
2466 insert_action(new Actions::DeleteBrowserItems());
2467 insert_action(new Actions::ReplaySong());
2468 insert_action(new Actions::PreviousSong());
2469 insert_action(new Actions::NextSong());
2470 insert_action(new Actions::Pause());
2471 insert_action(new Actions::Stop());
2472 insert_action(new Actions::ExecuteCommand());
2473 insert_action(new Actions::SavePlaylist());
2474 insert_action(new Actions::MoveSortOrderUp());
2475 insert_action(new Actions::MoveSortOrderDown());
2476 insert_action(new Actions::MoveSelectedItemsUp());
2477 insert_action(new Actions::MoveSelectedItemsDown());
2478 insert_action(new Actions::MoveSelectedItemsTo());
2479 insert_action(new Actions::Add());
2480 insert_action(new Actions::SeekForward());
2481 insert_action(new Actions::SeekBackward());
2482 insert_action(new Actions::ToggleDisplayMode());
2483 insert_action(new Actions::ToggleSeparatorsBetweenAlbums());
2484 insert_action(new Actions::ToggleLyricsFetcher());
2485 insert_action(new Actions::ToggleFetchingLyricsInBackground());
2486 insert_action(new Actions::TogglePlayingSongCentering());
2487 insert_action(new Actions::UpdateDatabase());
2488 insert_action(new Actions::JumpToPlayingSong());
2489 insert_action(new Actions::ToggleRepeat());
2490 insert_action(new Actions::Shuffle());
2491 insert_action(new Actions::ToggleRandom());
2492 insert_action(new Actions::StartSearching());
2493 insert_action(new Actions::SaveTagChanges());
2494 insert_action(new Actions::ToggleSingle());
2495 insert_action(new Actions::ToggleConsume());
2496 insert_action(new Actions::ToggleCrossfade());
2497 insert_action(new Actions::SetCrossfade());
2498 insert_action(new Actions::SetVolume());
2499 insert_action(new Actions::EditSong());
2500 insert_action(new Actions::EditLibraryTag());
2501 insert_action(new Actions::EditLibraryAlbum());
2502 insert_action(new Actions::EditDirectoryName());
2503 insert_action(new Actions::EditPlaylistName());
2504 insert_action(new Actions::EditLyrics());
2505 insert_action(new Actions::JumpToBrowser());
2506 insert_action(new Actions::JumpToMediaLibrary());
2507 insert_action(new Actions::JumpToPlaylistEditor());
2508 insert_action(new Actions::ToggleScreenLock());
2509 insert_action(new Actions::JumpToTagEditor());
2510 insert_action(new Actions::JumpToPositionInSong());
2511 insert_action(new Actions::ReverseSelection());
2512 insert_action(new Actions::RemoveSelection());
2513 insert_action(new Actions::SelectAlbum());
2514 insert_action(new Actions::AddSelectedItems());
2515 insert_action(new Actions::CropMainPlaylist());
2516 insert_action(new Actions::CropPlaylist());
2517 insert_action(new Actions::ClearMainPlaylist());
2518 insert_action(new Actions::ClearPlaylist());
2519 insert_action(new Actions::SortPlaylist());
2520 insert_action(new Actions::ReversePlaylist());
2521 insert_action(new Actions::ApplyFilter());
2522 insert_action(new Actions::Find());
2523 insert_action(new Actions::FindItemForward());
2524 insert_action(new Actions::FindItemBackward());
2525 insert_action(new Actions::NextFoundItem());
2526 insert_action(new Actions::PreviousFoundItem());
2527 insert_action(new Actions::ToggleFindMode());
2528 insert_action(new Actions::ToggleReplayGainMode());
2529 insert_action(new Actions::ToggleSpaceMode());
2530 insert_action(new Actions::ToggleAddMode());
2531 insert_action(new Actions::ToggleMouse());
2532 insert_action(new Actions::ToggleBitrateVisibility());
2533 insert_action(new Actions::AddRandomItems());
2534 insert_action(new Actions::ToggleBrowserSortMode());
2535 insert_action(new Actions::ToggleLibraryTagType());
2536 insert_action(new Actions::ToggleMediaLibrarySortMode());
2537 insert_action(new Actions::RefetchLyrics());
2538 insert_action(new Actions::RefetchArtistInfo());
2539 insert_action(new Actions::SetSelectedItemsPriority());
2540 insert_action(new Actions::FilterPlaylistOnPriorities());
2541 insert_action(new Actions::ShowSongInfo());
2542 insert_action(new Actions::ShowArtistInfo());
2543 insert_action(new Actions::ShowLyrics());
2544 insert_action(new Actions::Quit());
2545 insert_action(new Actions::NextScreen());
2546 insert_action(new Actions::PreviousScreen());
2547 insert_action(new Actions::ShowHelp());
2548 insert_action(new Actions::ShowPlaylist());
2549 insert_action(new Actions::ShowBrowser());
2550 insert_action(new Actions::ChangeBrowseMode());
2551 insert_action(new Actions::ShowSearchEngine());
2552 insert_action(new Actions::ResetSearchEngine());
2553 insert_action(new Actions::ShowMediaLibrary());
2554 insert_action(new Actions::ToggleMediaLibraryColumnsMode());
2555 insert_action(new Actions::ShowPlaylistEditor());
2556 insert_action(new Actions::ShowTagEditor());
2557 insert_action(new Actions::ShowOutputs());
2558 insert_action(new Actions::ShowVisualizer());
2559 insert_action(new Actions::ShowClock());
2560 insert_action(new Actions::ShowServerInfo());
2563 void seek()
2565 using Global::wHeader;
2566 using Global::wFooter;
2567 using Global::Timer;
2568 using Global::SeekingInProgress;
2570 if (!myPlaylist->currentSongLength())
2572 Statusbar::msg("Unknown item length");
2573 return;
2576 Progressbar::lock();
2577 Statusbar::lock();
2579 unsigned songpos = Status::State::elapsedTime();
2580 timeval t = Timer;
2582 int old_timeout = wFooter->getTimeout();
2583 wFooter->setTimeout(500);
2585 auto seekForward = Actions::get(Actions::Type::SeekForward);
2586 auto seekBackward = Actions::get(Actions::Type::SeekBackward);
2588 SeekingInProgress = true;
2589 while (true)
2591 Status::trace();
2592 myPlaylist->UpdateTimer();
2594 unsigned howmuch = Config.incremental_seeking ? (Timer.tv_sec-t.tv_sec)/2+Config.seek_time : Config.seek_time;
2596 Key input = Key::read(*wFooter);
2597 auto k = Bindings.get(input);
2598 if (k.first == k.second || !k.first->isSingle()) // no single action?
2599 break;
2600 auto a = k.first->action();
2601 if (a == seekForward)
2603 if (songpos < myPlaylist->currentSongLength())
2604 songpos = std::min(songpos + howmuch, myPlaylist->currentSongLength());
2606 else if (a == seekBackward)
2608 if (songpos > 0)
2610 if (songpos < howmuch)
2611 songpos = 0;
2612 else
2613 songpos -= howmuch;
2616 else
2617 break;
2619 *wFooter << NC::Format::Bold;
2620 std::string tracklength;
2621 if (Config.new_design)
2623 if (Config.display_remaining_time)
2625 tracklength = "-";
2626 tracklength += MPD::Song::ShowTime(myPlaylist->currentSongLength()-songpos);
2628 else
2629 tracklength = MPD::Song::ShowTime(songpos);
2630 tracklength += "/";
2631 tracklength += MPD::Song::ShowTime(myPlaylist->currentSongLength());
2632 *wHeader << NC::XY(0, 0) << tracklength << " ";
2633 wHeader->refresh();
2635 else
2637 tracklength = " [";
2638 if (Config.display_remaining_time)
2640 tracklength += "-";
2641 tracklength += MPD::Song::ShowTime(myPlaylist->currentSongLength()-songpos);
2643 else
2644 tracklength += MPD::Song::ShowTime(songpos);
2645 tracklength += "/";
2646 tracklength += MPD::Song::ShowTime(myPlaylist->currentSongLength());
2647 tracklength += "]";
2648 *wFooter << NC::XY(wFooter->getWidth()-tracklength.length(), 1) << tracklength;
2650 *wFooter << NC::Format::NoBold;
2651 Progressbar::draw(songpos, myPlaylist->currentSongLength());
2652 wFooter->refresh();
2654 SeekingInProgress = false;
2655 Mpd.Seek(myPlaylist->currentSongPosition(), songpos);
2657 wFooter->setTimeout(old_timeout);
2659 Progressbar::unlock();
2660 Statusbar::unlock();
2663 void findItem(const Find direction)
2665 using Global::wFooter;
2667 Searchable *w = dynamic_cast<Searchable *>(myScreen);
2668 assert(w);
2669 assert(w->allowsSearching());
2671 Statusbar::lock();
2672 Statusbar::put() << "Find " << (direction == Find::Forward ? "forward" : "backward") << ": ";
2673 std::string findme = wFooter->getString();
2674 Statusbar::unlock();
2676 if (!findme.empty())
2677 Statusbar::msg("Searching...");
2679 bool success = w->search(findme);
2681 if (findme.empty())
2682 return;
2684 if (success)
2685 Statusbar::msg("Searching finished");
2686 else
2687 Statusbar::msg("Unable to find \"%s\"", findme.c_str());
2689 if (direction == ::Find::Forward)
2690 w->nextFound(Config.wrapped_search);
2691 else
2692 w->prevFound(Config.wrapped_search);
2694 if (myScreen == myPlaylist)
2695 myPlaylist->EnableHighlighting();
2698 void listsChangeFinisher()
2700 if (myScreen == myLibrary
2701 || myScreen == myPlaylistEditor
2702 # ifdef HAVE_TAGLIB_H
2703 || myScreen == myTagEditor
2704 # endif // HAVE_TAGLIB_H
2707 if (myScreen->activeWindow() == &myLibrary->Tags)
2709 myLibrary->Albums.clear();
2710 myLibrary->Songs.clear();
2712 else if (myScreen->activeWindow() == &myLibrary->Albums)
2714 myLibrary->Songs.clear();
2716 else if (myScreen->isActiveWindow(myPlaylistEditor->Playlists))
2718 myPlaylistEditor->Content.clear();
2720 # ifdef HAVE_TAGLIB_H
2721 else if (myScreen->activeWindow() == myTagEditor->Dirs)
2723 myTagEditor->Tags->clear();
2725 # endif // HAVE_TAGLIB_H