1 /***************************************************************************
2 * Copyright (C) 2008-2013 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
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. *
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. *
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 ***************************************************************************/
24 #include <boost/locale/conversion.hpp>
25 #include <boost/lexical_cast.hpp>
36 #include "statusbar.h"
37 #include "utility/comparators.h"
43 #include "media_library.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"
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"
65 #endif // HAVE_TAGLIB_H
67 using namespace std::placeholders
;
68 using Global::myScreen
;
72 enum class Find
{ Forward
, Backward
};
74 std::map
<Actions::Type
, Actions::BaseAction
*> AvailableActions
;
76 void populateActions();
79 void findItem(const Find direction
);
80 void listsChangeFinisher();
86 bool OriginalStatusbarVisibility
;
87 bool ExitMainLoop
= false;
93 void validateScreenSize()
95 using Global::MainHeight
;
97 if (COLS
< 30 || MainHeight
< 5)
100 std::cout
<< "Screen is too small to handle ncmpcpp correctly\n";
105 void initializeScreens()
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
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
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)
187 // update internal screen dimensions
188 if (reload_main_window
)
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
202 MainHeight
= LINES
-(Config
.new_design
? 7 : 4);
204 validateScreenSize();
206 if (!Config
.header_visibility
)
208 if (!Config
.statusbar_visibility
)
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();
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
)
248 if (!Config
.statusbar_visibility
)
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
;
261 Statusbar::put() << question
<< " [" << NC::Format::Bold
<< 'y' << NC::Format::NoBold
<< '/' << NC::Format::Bold
<< 'n' << NC::Format::NoBold
<< "]";
268 answer
= wFooter
->readKey();
270 while (answer
!= 'y' && answer
!= 'n');
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");
285 BaseAction
*get(Actions::Type at
)
287 if (AvailableActions
.empty())
289 return AvailableActions
[at
];
292 BaseAction
*get(const std::string
&name
)
294 BaseAction
*result
= 0;
295 if (AvailableActions
.empty())
297 for (auto it
= AvailableActions
.begin(); it
!= AvailableActions
.end(); ++it
)
299 if (it
->second
->name() == name
)
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
)
328 if (m_mouse_event
.bstate
& BUTTON1_PRESSED
329 && m_mouse_event
.y
== LINES
-(Config
.statusbar_visibility
? 2 : 1)
332 if (Status::State::player() == MPD::psStop
)
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
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()
350 if (m_mouse_event
.bstate
& BUTTON2_PRESSED
)
351 get(Type::VolumeDown
)->execute();
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
);
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
);
380 size_t pos
= pl
.choice();
381 if (MPD::Song
*s
= pl
.getSong(pos
))
383 std::string artist
= s
->getArtist();
386 s
= pl
.getSong(--pos
);
387 if (!s
|| s
->getArtist() != artist
)
394 bool ScrollUpAlbum::canBeRun() const
396 return proxySongList(myScreen
);
399 void ScrollUpAlbum::run()
401 auto pl
= proxySongList(myScreen
);
403 size_t pos
= pl
.choice();
404 if (MPD::Song
*s
= pl
.getSong(pos
))
406 std::string album
= s
->getAlbum();
409 s
= pl
.getSong(--pos
);
410 if (!s
|| s
->getAlbum() != album
)
417 bool ScrollDownArtist::canBeRun() const
419 return proxySongList(myScreen
);
422 void ScrollDownArtist::run()
424 auto pl
= proxySongList(myScreen
);
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
)
440 bool ScrollDownAlbum::canBeRun() const
442 return proxySongList(myScreen
);
445 void ScrollDownAlbum::run()
447 auto pl
= proxySongList(myScreen
);
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
)
465 myScreen
->scroll(NC::Scroll::PageUp
);
466 listsChangeFinisher();
471 myScreen
->scroll(NC::Scroll::PageDown
);
472 listsChangeFinisher();
477 myScreen
->scroll(NC::Scroll::Home
);
478 listsChangeFinisher();
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();
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
570 && myLockedScreen
!= myScreen
571 && myScreen
->isMergable();
574 void MasterScreen::run()
576 using Global::myInactiveScreen
;
577 using Global::myLockedScreen
;
579 myInactiveScreen
= myScreen
;
580 myScreen
= myLockedScreen
;
584 bool SlaveScreen::canBeRun() const
586 using Global::myLockedScreen
;
587 using Global::myInactiveScreen
;
589 return myLockedScreen
591 && myLockedScreen
== myScreen
592 && myScreen
->isMergable();
595 void SlaveScreen::run()
597 using Global::myInactiveScreen
;
598 using Global::myLockedScreen
;
600 myScreen
= myInactiveScreen
;
601 myInactiveScreen
= myLockedScreen
;
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?";
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
);
661 question
+= ToString(wideShorten(ToWString(iname
), COLS
-question
.size()-10));
664 bool yes
= askYesNoQuestion(question
, Status::trace
);
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());
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
));
688 if (myBrowser
->isLocal())
689 myBrowser
->GetDirectory(myBrowser
->CurrentDir());
691 Mpd
.UpdateDirectory(myBrowser
->CurrentDir());
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?";
711 question
= "Delete playlist \"";
712 question
+= ToString(wideShorten(ToWString(myPlaylistEditor
->Playlists
.current().value()), COLS
-question
.size()-10));
715 bool yes
= askYesNoQuestion(question
, Status::trace
);
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");
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()
750 void SavePlaylist::run()
752 using Global::wFooter
;
755 Statusbar::put() << "Save playlist as: ";
756 std::string playlist_name
= wFooter
->getString();
758 if (playlist_name
.find("/") != std::string::npos
)
760 Statusbar::msg("Playlist name must not contain slashes");
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());
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
);
787 Mpd
.DeletePlaylist(playlist_name
);
788 Mpd
.SavePlaylist(playlist_name
);
789 Statusbar::msg("Playlist overwritten");
792 Statusbar::msg("Aborted");
793 if (myScreen
== myPlaylist
)
794 myPlaylist
->EnableHighlighting();
801 if (!myBrowser
->isLocal()
802 && myBrowser
->CurrentDir() == "/"
803 && !myBrowser
->main().empty())
804 myBrowser
->GetDirectory(myBrowser
->CurrentDir());
812 void ExecuteCommand::run()
814 using Global::wFooter
;
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
);
821 if (cmd_name
.empty())
823 auto cmd
= Bindings
.findCommand(cmd_name
);
826 Statusbar::msg(1, "Executing %s...", cmd_name
.c_str());
827 cmd
->binding().execute();
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
));
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();
930 using Global::wFooter
;
933 Statusbar::put() << (myScreen
== myPlaylistEditor
? "Add to playlist: " : "Add: ");
934 std::string path
= wFooter
->getString();
938 Statusbar::put() << "Adding...";
940 if (myScreen
== myPlaylistEditor
)
941 Mpd
.AddToPlaylist(myPlaylistEditor
->Playlists
.current().value(), path
);
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
);
958 bool SeekForward::canBeRun() const
960 return Status::State::player() != MPD::psStop
&& myPlaylist
->currentSongLength() > 0;
963 void SeekForward::run()
968 bool SeekBackward::canBeRun() const
970 return Status::State::player() != MPD::psStop
&& myPlaylist
->currentSongLength() > 0;
973 void SeekBackward::run()
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()));
999 myPlaylist
->main().setTitle("");
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()));
1027 myPlaylistEditor
->Content
.setItemDisplayer(std::bind(Display::Songs
, _1
, myPlaylistEditor
->contentProxyList(), Config
.song_list_format
));
1031 bool ToggleSeparatorsBetweenAlbums::canBeRun() const
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
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
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
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());
1110 else if (myScreen
== myLibrary
)
1112 myLibrary
->LocateSong(myPlaylist
->nowPlayingSong());
1116 void ToggleRepeat::run()
1118 Mpd
.SetRepeat(!Status::State::repeat());
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
;
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
;
1191 Statusbar::put() << "Set crossfade to: ";
1192 std::string crossfade
= wFooter
->getString(3);
1193 Statusbar::unlock();
1194 int cf
= boost::lexical_cast
<int>(crossfade
);
1197 Config
.crossfade_time
= cf
;
1198 Mpd
.SetCrossfade(cf
);
1202 void SetVolume::run()
1204 using Global::wFooter
;
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();
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();
1245 # endif // HAVE_TAGLIB_H
1248 void EditLibraryTag::run()
1250 # ifdef HAVE_TAGLIB_H
1251 using Global::wFooter
;
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...");
1261 Mpd
.AddSearch(Config
.media_lib_primary_tag
, myLibrary
->Tags
.current().value().tag());
1262 MPD::MutableSong::SetFunction set
= tagTypeToSetFunction(Config
.media_lib_primary_tag
);
1264 bool success
= true;
1265 std::string dir_to_update
;
1266 Mpd
.CommitSearchSongs([set
, &dir_to_update
, &new_tag
, &success
](MPD::Song
&&s
) {
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());
1279 if (dir_to_update
.empty())
1280 dir_to_update
= s
.getURI();
1282 dir_to_update
= getSharedDirectory(dir_to_update
, s
.getURI());
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();
1301 # endif // HAVE_TAGLIB_H
1304 void EditLibraryAlbum::run()
1306 # ifdef HAVE_TAGLIB_H
1307 using Global::wFooter
;
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())
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());
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());
1329 f
.tag()->setAlbum(ToWString(new_album
));
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());
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
;
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());
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
;
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());
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();
1442 old_name
= myBrowser
->main().current().value().name
;
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()
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();
1510 Statusbar::msg("Screen unlocked");
1514 int part
= Config
.locked_screen_width_part
*100;
1515 if (Config
.ask_for_locked_screen_width_part
)
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())
1523 part
= boost::lexical_cast
<int>(str_part
);
1525 if (part
< 20 || part
> 80)
1527 Statusbar::msg("Number is out of range");
1530 Config
.locked_screen_width_part
= part
/100.0;
1531 if (myScreen
->lock())
1532 Statusbar::msg("Screen locked (with %d%% width)", part
);
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();
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();
1568 Statusbar::put() << "Position to go (in %/mm:ss/seconds(s)): ";
1569 std::string position
= wFooter
->getString();
1570 Statusbar::unlock();
1572 if (position
.empty())
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
);
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
);
1591 Statusbar::msg("Out of bounds, 0-%d possible for seconds, %d given", s
.getDuration(), newpos
);
1595 newpos
= boost::lexical_cast
<int>(position
);
1597 Mpd
.Seek(s
.getPosition(), s
.getDuration()*newpos
/100.0);
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);
1647 s
= pl
.getSong(--pos
);
1648 if (!s
|| s
->getAlbum() != album
)
1651 pl
.setSelected(pos
, true);
1655 while (pos
< pl
.size() - 1)
1657 s
= pl
.getSong(++pos
);
1658 if (!s
|| s
->getAlbum() != album
)
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()
1680 if (Config
.ask_before_clearing_main_playlist
)
1681 yes
= askYesNoQuestion("Do you really want to crop main playlist?", Status::trace
);
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();
1700 if (Config
.ask_before_clearing_main_playlist
)
1701 yes
= askYesNoQuestion("Do you really want to crop playlist \"" + playlist
+ "\"?", Status::trace
);
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()
1714 if (Config
.ask_before_clearing_main_playlist
)
1715 yes
= askYesNoQuestion("Do you really want to clear main playlist?", Status::trace
);
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();
1736 if (Config
.ask_before_clearing_main_playlist
)
1737 yes
= askYesNoQuestion("Do you really want to clear playlist \"" + playlist
+ "\"?", Status::trace
);
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();
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();
1791 myPlaylist
->main().clearFilterResults();
1792 Statusbar::msg("Filtering disabled");
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;
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
1823 using Global::wFooter
;
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");
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
;
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]";
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
;
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] ";
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
;
1955 tag_type
= charToTagType(answer
);
1956 tag_type_str
= boost::locale::to_lower(tagTypeToString(tag_type
));
1959 tag_type_str
= "song";
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
)
1979 Config
.browser_sort_mode
= smMTime
;
1980 Statusbar::msg("Sort songs by: Modification time");
1983 Config
.browser_sort_mode
= smCustomFormat
;
1984 Statusbar::msg("Sort songs by: Custom format");
1986 case smCustomFormat
:
1987 Config
.browser_sort_mode
= smName
;
1988 Statusbar::msg("Sort songs by: Name");
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
;
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] ";
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
?
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();
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
;
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
;
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");
2093 return myScreen
== myPlaylist
&& !myPlaylist
->main().empty();
2096 void SetSelectedItemsPriority::run()
2098 using Global::wFooter
;
2101 Statusbar::put() << "Set priority [0-255]: ";
2102 std::string strprio
= wFooter
->getString();
2103 Statusbar::unlock();
2104 if (!isInteger(strprio
.c_str(), true))
2106 int prio
= boost::lexical_cast
<int>(strprio
);
2107 if (prio
< 0 || prio
> 255)
2109 Statusbar::msg("Number is out of range");
2112 myPlaylist
->SetSelectedItemsPriority(prio
);
2115 bool FilterPlaylistOnPriorities::canBeRun() const
2117 return myScreen
== myPlaylist
;
2120 void FilterPlaylistOnPriorities::run()
2122 using Global::wFooter
;
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))
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
);
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();
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();
2174 auto s
= currentSong(myScreen
);
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();
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();
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();
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()
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
;
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
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
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
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();
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());
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");
2576 Progressbar::lock();
2579 unsigned songpos
= Status::State::elapsedTime();
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;
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?
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
)
2610 if (songpos
< howmuch
)
2619 *wFooter
<< NC::Format::Bold
;
2620 std::string tracklength
;
2621 if (Config
.new_design
)
2623 if (Config
.display_remaining_time
)
2626 tracklength
+= MPD::Song::ShowTime(myPlaylist
->currentSongLength()-songpos
);
2629 tracklength
= MPD::Song::ShowTime(songpos
);
2631 tracklength
+= MPD::Song::ShowTime(myPlaylist
->currentSongLength());
2632 *wHeader
<< NC::XY(0, 0) << tracklength
<< " ";
2638 if (Config
.display_remaining_time
)
2641 tracklength
+= MPD::Song::ShowTime(myPlaylist
->currentSongLength()-songpos
);
2644 tracklength
+= MPD::Song::ShowTime(songpos
);
2646 tracklength
+= MPD::Song::ShowTime(myPlaylist
->currentSongLength());
2648 *wFooter
<< NC::XY(wFooter
->getWidth()-tracklength
.length(), 1) << tracklength
;
2650 *wFooter
<< NC::Format::NoBold
;
2651 Progressbar::draw(songpos
, myPlaylist
->currentSongLength());
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
);
2669 assert(w
->allowsSearching());
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
);
2685 Statusbar::msg("Searching finished");
2687 Statusbar::msg("Unable to find \"%s\"", findme
.c_str());
2689 if (direction
== ::Find::Forward
)
2690 w
->nextFound(Config
.wrapped_search
);
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