1 /***************************************************************************
2 * Copyright (C) 2008-2014 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/array.hpp>
25 #include <boost/bind.hpp>
26 #include <boost/locale/conversion.hpp>
27 #include <boost/lexical_cast.hpp>
30 #include <readline/readline.h>
39 #include "statusbar.h"
40 #include "utility/comparators.h"
41 #include "utility/conversion.h"
47 #include "media_library.h"
51 #include "playlist_editor.h"
52 #include "sort_playlist.h"
53 #include "search_engine.h"
54 #include "sel_items_adder.h"
55 #include "server_info.h"
56 #include "song_info.h"
58 #include "utility/string.h"
59 #include "utility/type_conversions.h"
60 #include "tag_editor.h"
61 #include "tiny_tag_editor.h"
62 #include "visualizer.h"
69 #endif // HAVE_TAGLIB_H
71 using Global::myScreen
;
75 enum class Find
{ Forward
, Backward
};
78 Actions::BaseAction
*, static_cast<size_t>(Actions::Type::_numberOfActions
)
81 void populateActions();
84 void findItem(const Find direction
);
85 void listsChangeFinisher();
91 bool OriginalStatusbarVisibility
;
92 bool ExitMainLoop
= false;
98 void validateScreenSize()
100 using Global::MainHeight
;
102 if (COLS
< 30 || MainHeight
< 5)
105 std::cout
<< "Screen is too small to handle ncmpcpp correctly\n";
110 void initializeScreens()
113 myPlaylist
= new Playlist
;
114 myBrowser
= new Browser
;
115 mySearcher
= new SearchEngine
;
116 myLibrary
= new MediaLibrary
;
117 myPlaylistEditor
= new PlaylistEditor
;
118 myLyrics
= new Lyrics
;
119 mySelectedItemsAdder
= new SelectedItemsAdder
;
120 mySongInfo
= new SongInfo
;
121 myServerInfo
= new ServerInfo
;
122 mySortPlaylistDialog
= new SortPlaylistDialog
;
124 # ifdef HAVE_CURL_CURL_H
125 myLastfm
= new Lastfm
;
126 # endif // HAVE_CURL_CURL_H
128 # ifdef HAVE_TAGLIB_H
129 myTinyTagEditor
= new TinyTagEditor
;
130 myTagEditor
= new TagEditor
;
131 # endif // HAVE_TAGLIB_H
133 # ifdef ENABLE_VISUALIZER
134 myVisualizer
= new Visualizer
;
135 # endif // ENABLE_VISUALIZER
137 # ifdef ENABLE_OUTPUTS
138 myOutputs
= new Outputs
;
139 # endif // ENABLE_OUTPUTS
143 # endif // ENABLE_CLOCK
147 void setResizeFlags()
149 myHelp
->hasToBeResized
= 1;
150 myPlaylist
->hasToBeResized
= 1;
151 myBrowser
->hasToBeResized
= 1;
152 mySearcher
->hasToBeResized
= 1;
153 myLibrary
->hasToBeResized
= 1;
154 myPlaylistEditor
->hasToBeResized
= 1;
155 myLyrics
->hasToBeResized
= 1;
156 mySelectedItemsAdder
->hasToBeResized
= 1;
157 mySongInfo
->hasToBeResized
= 1;
158 myServerInfo
->hasToBeResized
= 1;
159 mySortPlaylistDialog
->hasToBeResized
= 1;
161 # ifdef HAVE_CURL_CURL_H
162 myLastfm
->hasToBeResized
= 1;
163 # endif // HAVE_CURL_CURL_H
165 # ifdef HAVE_TAGLIB_H
166 myTinyTagEditor
->hasToBeResized
= 1;
167 myTagEditor
->hasToBeResized
= 1;
168 # endif // HAVE_TAGLIB_H
170 # ifdef ENABLE_VISUALIZER
171 myVisualizer
->hasToBeResized
= 1;
172 # endif // ENABLE_VISUALIZER
174 # ifdef ENABLE_OUTPUTS
175 myOutputs
->hasToBeResized
= 1;
176 # endif // ENABLE_OUTPUTS
179 myClock
->hasToBeResized
= 1;
180 # endif // ENABLE_CLOCK
183 void resizeScreen(bool reload_main_window
)
185 using Global::MainHeight
;
186 using Global::wHeader
;
187 using Global::wFooter
;
189 # if defined(USE_PDCURSES)
192 // update internal screen dimensions
193 if (reload_main_window
)
195 rl_resize_terminal();
201 MainHeight
= LINES
-(Config
.design
== Design::Alternative
? 7 : 4);
203 validateScreenSize();
205 if (!Config
.header_visibility
)
207 if (!Config
.statusbar_visibility
)
212 applyToVisibleWindows(&BaseScreen::resize
);
214 if (Config
.header_visibility
|| Config
.design
== Design::Alternative
)
215 wHeader
->resize(COLS
, HeaderHeight
);
217 FooterStartY
= LINES
-(Config
.statusbar_visibility
? 2 : 1);
218 wFooter
->moveTo(0, FooterStartY
);
219 wFooter
->resize(COLS
, Config
.statusbar_visibility
? 2 : 1);
221 applyToVisibleWindows(&BaseScreen::refresh
);
223 Status::Changes::elapsedTime(false);
224 Status::Changes::playerState();
225 // Note: routines for drawing separator if alternative user
226 // interface is active and header is hidden are placed in
227 // NcmpcppStatusChanges.StatusFlags
228 Status::Changes::flags();
234 void setWindowsDimensions()
236 using Global::MainStartY
;
237 using Global::MainHeight
;
239 MainStartY
= Config
.design
== Design::Alternative
? 5 : 2;
240 MainHeight
= LINES
-(Config
.design
== Design::Alternative
? 7 : 4);
242 if (!Config
.header_visibility
)
247 if (!Config
.statusbar_visibility
)
250 HeaderHeight
= Config
.design
== Design::Alternative
? (Config
.header_visibility
? 5 : 3) : 1;
251 FooterStartY
= LINES
-(Config
.statusbar_visibility
? 2 : 1);
252 FooterHeight
= Config
.statusbar_visibility
? 2 : 1;
255 bool askYesNoQuestion(const boost::format
&fmt
, void (*callback
)())
257 using Global::wFooter
;
260 Statusbar::put() << fmt
.str() << " [" << NC::Format::Bold
<< 'y' << NC::Format::NoBold
<< '/' << NC::Format::Bold
<< 'n' << NC::Format::NoBold
<< "]";
267 answer
= wFooter
->readKey();
269 while (answer
!= 'y' && answer
!= 'n');
271 return answer
== 'y';
274 bool isMPDMusicDirSet()
276 if (Config
.mpd_music_dir
.empty())
278 Statusbar::print("Proper mpd_music_dir variable has to be set in configuration file");
284 BaseAction
&get(Actions::Type at
)
286 if (AvailableActions
[1] == nullptr)
288 BaseAction
*action
= AvailableActions
[static_cast<size_t>(at
)];
289 // action should be always present if action type in queried
290 assert(action
!= nullptr);
294 BaseAction
*get(const std::string
&name
)
296 BaseAction
*result
= 0;
297 if (AvailableActions
[1] == nullptr)
299 for (auto it
= AvailableActions
.begin(); it
!= AvailableActions
.end(); ++it
)
301 if (*it
!= nullptr && (*it
)->name() == name
)
310 bool MouseEvent::canBeRun() const
312 return Config
.mouse_support
;
315 void MouseEvent::run()
317 using Global::VolumeState
;
319 m_old_mouse_event
= m_mouse_event
;
320 getmouse(&m_mouse_event
);
322 # if NCURSES_MOUSE_VERSION == 1
323 // workaround shitty ncurses behavior introduced in >=5.8, when we mysteriously get
324 // a few times after ncmpcpp startup 2^27 code instead of BUTTON{1,3}_RELEASED. since that
325 // 2^27 thing shows constantly instead of BUTTON2_PRESSED, it was redefined to be recognized
326 // as BUTTON5_PRESSED. but clearly we don't want to trigger behavior bound to BUTTON5
327 // after BUTTON{1,3} was pressed. so, here is the workaround: if last event was BUTTON{1,3}_PRESSED,
328 // we MUST get BUTTON{1,3}_RELEASED afterwards. if we get BUTTON5_PRESSED, erroneus behavior
329 // is about to occur and we need to prevent that.
330 if (m_old_mouse_event
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
) && m_mouse_event
.bstate
& BUTTON5_PRESSED
)
332 # endif // NCURSES_MOUSE_VERSION == 1
334 if (m_mouse_event
.bstate
& BUTTON1_PRESSED
335 && m_mouse_event
.y
== LINES
-(Config
.statusbar_visibility
? 2 : 1)
338 if (Status::State::player() == MPD::psStop
)
340 Mpd
.Seek(Status::State::currentSongPosition(),
341 Status::State::totalTime()*m_mouse_event
.x
/double(COLS
));
343 else if (m_mouse_event
.bstate
& BUTTON1_PRESSED
344 && (Config
.statusbar_visibility
|| Config
.design
== Design::Alternative
)
345 && Status::State::player() != MPD::psStop
346 && m_mouse_event
.y
== (Config
.design
== Design::Alternative
? 1 : LINES
-1)
347 && m_mouse_event
.x
< 9
352 else if ((m_mouse_event
.bstate
& BUTTON5_PRESSED
|| m_mouse_event
.bstate
& BUTTON4_PRESSED
)
353 && (Config
.header_visibility
|| Config
.design
== Design::Alternative
)
354 && m_mouse_event
.y
== 0 && size_t(m_mouse_event
.x
) > COLS
-VolumeState
.length()
357 if (m_mouse_event
.bstate
& BUTTON5_PRESSED
)
358 get(Type::VolumeDown
).execute();
360 get(Type::VolumeUp
).execute();
362 else if (m_mouse_event
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
| BUTTON4_PRESSED
| BUTTON5_PRESSED
))
363 myScreen
->mouseButtonPressed(m_mouse_event
);
368 myScreen
->scroll(NC::Scroll::Up
);
369 listsChangeFinisher();
372 void ScrollDown::run()
374 myScreen
->scroll(NC::Scroll::Down
);
375 listsChangeFinisher();
378 bool ScrollUpArtist::canBeRun() const
380 return proxySongList(myScreen
);
383 void ScrollUpArtist::run()
385 auto pl
= proxySongList(myScreen
);
389 size_t pos
= pl
.choice();
390 if (MPD::Song
*s
= pl
.getSong(pos
))
392 std::string artist
= s
->getArtist();
395 s
= pl
.getSong(--pos
);
396 if (!s
|| s
->getArtist() != artist
)
403 bool ScrollUpAlbum::canBeRun() const
405 return proxySongList(myScreen
);
408 void ScrollUpAlbum::run()
410 auto pl
= proxySongList(myScreen
);
414 size_t pos
= pl
.choice();
415 if (MPD::Song
*s
= pl
.getSong(pos
))
417 std::string album
= s
->getAlbum();
420 s
= pl
.getSong(--pos
);
421 if (!s
|| s
->getAlbum() != album
)
428 bool ScrollDownArtist::canBeRun() const
430 return proxySongList(myScreen
);
433 void ScrollDownArtist::run()
435 auto pl
= proxySongList(myScreen
);
439 size_t pos
= pl
.choice();
440 if (MPD::Song
*s
= pl
.getSong(pos
))
442 std::string artist
= s
->getArtist();
443 while (pos
< pl
.size() - 1)
445 s
= pl
.getSong(++pos
);
446 if (!s
|| s
->getArtist() != artist
)
453 bool ScrollDownAlbum::canBeRun() const
455 return proxySongList(myScreen
);
458 void ScrollDownAlbum::run()
460 auto pl
= proxySongList(myScreen
);
464 size_t pos
= pl
.choice();
465 if (MPD::Song
*s
= pl
.getSong(pos
))
467 std::string album
= s
->getAlbum();
468 while (pos
< pl
.size() - 1)
470 s
= pl
.getSong(++pos
);
471 if (!s
|| s
->getAlbum() != album
)
480 myScreen
->scroll(NC::Scroll::PageUp
);
481 listsChangeFinisher();
486 myScreen
->scroll(NC::Scroll::PageDown
);
487 listsChangeFinisher();
492 myScreen
->scroll(NC::Scroll::Home
);
493 listsChangeFinisher();
498 myScreen
->scroll(NC::Scroll::End
);
499 listsChangeFinisher();
502 void ToggleInterface::run()
504 switch (Config
.design
)
506 case Design::Classic
:
507 Config
.design
= Design::Alternative
;
508 Config
.statusbar_visibility
= false;
510 case Design::Alternative
:
511 Config
.design
= Design::Classic
;
512 Config
.statusbar_visibility
= OriginalStatusbarVisibility
;
515 setWindowsDimensions();
516 Progressbar::unlock();
519 Status::Changes::mixer();
520 Status::Changes::elapsedTime(false);
521 Statusbar::printf("User interface: %1%", Config
.design
);
524 bool JumpToParentDirectory::canBeRun() const
526 return (myScreen
== myBrowser
)
527 # ifdef HAVE_TAGLIB_H
528 || (myScreen
->activeWindow() == myTagEditor
->Dirs
)
529 # endif // HAVE_TAGLIB_H
533 void JumpToParentDirectory::run()
535 if (myScreen
== myBrowser
)
537 if (myBrowser
->CurrentDir() != "/")
539 myBrowser
->main().reset();
540 myBrowser
->enterPressed();
543 # ifdef HAVE_TAGLIB_H
544 else if (myScreen
== myTagEditor
)
546 if (myTagEditor
->CurrentDir() != "/")
548 myTagEditor
->Dirs
->reset();
549 myTagEditor
->enterPressed();
552 # endif // HAVE_TAGLIB_H
555 void PressEnter::run()
557 myScreen
->enterPressed();
560 void PressSpace::run()
562 myScreen
->spacePressed();
565 bool PreviousColumn::canBeRun() const
567 auto hc
= hasColumns(myScreen
);
568 return hc
&& hc
->previousColumnAvailable();
571 void PreviousColumn::run()
573 hasColumns(myScreen
)->previousColumn();
576 bool NextColumn::canBeRun() const
578 auto hc
= hasColumns(myScreen
);
579 return hc
&& hc
->nextColumnAvailable();
582 void NextColumn::run()
584 hasColumns(myScreen
)->nextColumn();
587 bool MasterScreen::canBeRun() const
589 using Global::myLockedScreen
;
590 using Global::myInactiveScreen
;
592 return myLockedScreen
594 && myLockedScreen
!= myScreen
595 && myScreen
->isMergable();
598 void MasterScreen::run()
600 using Global::myInactiveScreen
;
601 using Global::myLockedScreen
;
603 myInactiveScreen
= myScreen
;
604 myScreen
= myLockedScreen
;
608 bool SlaveScreen::canBeRun() const
610 using Global::myLockedScreen
;
611 using Global::myInactiveScreen
;
613 return myLockedScreen
615 && myLockedScreen
== myScreen
616 && myScreen
->isMergable();
619 void SlaveScreen::run()
621 using Global::myInactiveScreen
;
622 using Global::myLockedScreen
;
624 myScreen
= myInactiveScreen
;
625 myInactiveScreen
= myLockedScreen
;
631 int volume
= std::min(Status::State::volume()+Config
.volume_change_step
, 100u);
632 Mpd
.SetVolume(volume
);
635 void VolumeDown::run()
637 int volume
= std::max(int(Status::State::volume()-Config
.volume_change_step
), 0);
638 Mpd
.SetVolume(volume
);
641 bool DeletePlaylistItems::canBeRun() const
643 return (myScreen
== myPlaylist
&& !myPlaylist
->main().empty())
644 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
) && !myPlaylistEditor
->Content
.empty());
647 void DeletePlaylistItems::run()
649 if (myScreen
== myPlaylist
)
651 Statusbar::print("Deleting items...");
652 auto delete_fun
= boost::bind(&MPD::Connection::Delete
, _1
, _2
);
653 deleteSelectedSongs(myPlaylist
->main(), delete_fun
);
654 Statusbar::print("Item(s) deleted");
656 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Content
))
658 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
659 auto delete_fun
= boost::bind(&MPD::Connection::PlaylistDelete
, _1
, playlist
, _2
);
660 Statusbar::print("Deleting items...");
661 deleteSelectedSongs(myPlaylistEditor
->Content
, delete_fun
);
662 Statusbar::print("Item(s) deleted");
666 bool DeleteBrowserItems::canBeRun() const
668 auto check_if_deletion_allowed
= []() {
669 if (Config
.allow_for_physical_item_deletion
)
673 Statusbar::print("Flag \"allow_for_physical_item_deletion\" needs to be enabled in configuration file");
677 return myScreen
== myBrowser
678 && !myBrowser
->main().empty()
679 && isMPDMusicDirSet()
680 && check_if_deletion_allowed();
683 void DeleteBrowserItems::run()
685 boost::format question
;
686 if (hasSelected(myBrowser
->main().begin(), myBrowser
->main().end()))
687 question
= boost::format("Delete selected items?");
690 MPD::Item
&item
= myBrowser
->main().current().value();
691 std::string iname
= item
.type
== MPD::itSong
? item
.song
->getName() : item
.name
;
692 question
= boost::format("Delete %1% \"%2%\"?")
693 % itemTypeToString(item
.type
) % wideShorten(iname
, COLS
-question
.size()-10);
695 bool yes
= askYesNoQuestion(question
, Status::trace
);
699 auto list
= getSelectedOrCurrent(myBrowser
->main().begin(), myBrowser
->main().end(), myBrowser
->main().currentI());
700 for (auto it
= list
.begin(); it
!= list
.end(); ++it
)
702 const MPD::Item
&i
= (*it
)->value();
703 std::string iname
= i
.type
== MPD::itSong
? i
.song
->getName() : i
.name
;
705 if (myBrowser
->deleteItem(i
, errmsg
))
707 const char msg
[] = "\"%1%\" deleted";
708 Statusbar::printf(msg
, wideShorten(iname
, COLS
-const_strlen(msg
)));
712 Statusbar::print(errmsg
);
719 if (myBrowser
->isLocal())
720 myBrowser
->GetDirectory(myBrowser
->CurrentDir());
722 Mpd
.UpdateDirectory(myBrowser
->CurrentDir());
726 Statusbar::print("Aborted");
729 bool DeleteStoredPlaylist::canBeRun() const
731 return myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
);
734 void DeleteStoredPlaylist::run()
736 if (myPlaylistEditor
->Playlists
.empty())
738 boost::format question
;
739 if (hasSelected(myPlaylistEditor
->Playlists
.begin(), myPlaylistEditor
->Playlists
.end()))
740 question
= boost::format("Delete selected playlists?");
742 question
= boost::format("Delete playlist \"%1%\"?")
743 % wideShorten(myPlaylistEditor
->Playlists
.current().value(), COLS
-question
.size()-10);
744 bool yes
= askYesNoQuestion(question
, Status::trace
);
747 auto list
= getSelectedOrCurrent(myPlaylistEditor
->Playlists
.begin(), myPlaylistEditor
->Playlists
.end(), myPlaylistEditor
->Playlists
.currentI());
748 for (auto it
= list
.begin(); it
!= list
.end(); ++it
)
749 Mpd
.DeletePlaylist((*it
)->value());
750 Statusbar::printf("%1% deleted", list
.size() == 1 ? "Playlist" : "Playlists");
751 // force playlists update. this happens automatically, but only after call
752 // to Key::read, therefore when we call PlaylistEditor::Update, it won't
753 // yet see it, so let's point that it needs to update it.
754 myPlaylistEditor
->requestPlaylistsUpdate();
757 Statusbar::print("Aborted");
760 void ReplaySong::run()
762 if (Status::State::player() != MPD::psStop
)
763 Mpd
.Seek(Status::State::currentSongPosition(), 0);
766 void PreviousSong::run()
781 void SavePlaylist::run()
783 using Global::wFooter
;
786 Statusbar::put() << "Save playlist as: ";
787 std::string playlist_name
= wFooter
->getString();
789 if (playlist_name
.find("/") != std::string::npos
)
791 Statusbar::print("Playlist name must not contain slashes");
794 if (!playlist_name
.empty())
796 if (myPlaylist
->main().isFiltered())
798 Mpd
.StartCommandsList();
799 for (size_t i
= 0; i
< myPlaylist
->main().size(); ++i
)
800 Mpd
.AddToPlaylist(playlist_name
, myPlaylist
->main()[i
].value());
801 Mpd
.CommitCommandsList();
802 Statusbar::printf("Filtered items added to playlist \"%1%\"", playlist_name
);
808 Mpd
.SavePlaylist(playlist_name
);
809 Statusbar::printf("Playlist saved as \"%1%\"", playlist_name
);
811 catch (MPD::ServerError
&e
)
813 if (e
.code() == MPD_SERVER_ERROR_EXIST
)
815 bool yes
= askYesNoQuestion(
816 boost::format("Playlist \"%1%\" already exists, overwrite?") % playlist_name
,
821 Mpd
.DeletePlaylist(playlist_name
);
822 Mpd
.SavePlaylist(playlist_name
);
823 Statusbar::print("Playlist overwritten");
826 Statusbar::print("Aborted");
827 if (myScreen
== myPlaylist
)
828 myPlaylist
->EnableHighlighting();
835 if (!myBrowser
->isLocal()
836 && myBrowser
->CurrentDir() == "/"
837 && !myBrowser
->main().empty())
838 myBrowser
->GetDirectory(myBrowser
->CurrentDir());
846 void ExecuteCommand::run()
848 using Global::wFooter
;
850 Statusbar::put() << NC::Format::Bold
<< ":" << NC::Format::NoBold
;
851 wFooter
->setGetStringHelper(Statusbar::Helpers::TryExecuteImmediateCommand());
852 std::string cmd_name
= wFooter
->getString();
853 wFooter
->setGetStringHelper(Statusbar::Helpers::getString
);
855 if (cmd_name
.empty())
857 auto cmd
= Bindings
.findCommand(cmd_name
);
860 Statusbar::printf(1, "Executing %1%...", cmd_name
);
861 bool res
= cmd
->binding().execute();
862 Statusbar::printf("Execution of command \"%1%\" %2%.",
863 cmd_name
, res
? "successful" : "unsuccessful"
867 Statusbar::printf("No command named \"%1%\"", cmd_name
);
870 bool MoveSortOrderUp::canBeRun() const
872 return myScreen
== mySortPlaylistDialog
;
875 void MoveSortOrderUp::run()
877 mySortPlaylistDialog
->moveSortOrderUp();
880 bool MoveSortOrderDown::canBeRun() const
882 return myScreen
== mySortPlaylistDialog
;
885 void MoveSortOrderDown::run()
887 mySortPlaylistDialog
->moveSortOrderDown();
890 bool MoveSelectedItemsUp::canBeRun() const
892 return ((myScreen
== myPlaylist
893 && !myPlaylist
->main().empty()
894 && !myPlaylist
->isFiltered())
895 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
)
896 && !myPlaylistEditor
->Content
.empty()
897 && !myPlaylistEditor
->isContentFiltered()));
900 void MoveSelectedItemsUp::run()
902 if (myScreen
== myPlaylist
)
904 moveSelectedItemsUp(myPlaylist
->main(), boost::bind(&MPD::Connection::Move
, _1
, _2
, _3
));
906 else if (myScreen
== myPlaylistEditor
)
908 assert(!myPlaylistEditor
->Playlists
.empty());
909 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
910 auto move_fun
= boost::bind(&MPD::Connection::PlaylistMove
, _1
, playlist
, _2
, _3
);
911 moveSelectedItemsUp(myPlaylistEditor
->Content
, move_fun
);
915 bool MoveSelectedItemsDown::canBeRun() const
917 return ((myScreen
== myPlaylist
918 && !myPlaylist
->main().empty()
919 && !myPlaylist
->isFiltered())
920 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
)
921 && !myPlaylistEditor
->Content
.empty()
922 && !myPlaylistEditor
->isContentFiltered()));
925 void MoveSelectedItemsDown::run()
927 if (myScreen
== myPlaylist
)
929 moveSelectedItemsDown(myPlaylist
->main(), boost::bind(&MPD::Connection::Move
, _1
, _2
, _3
));
931 else if (myScreen
== myPlaylistEditor
)
933 assert(!myPlaylistEditor
->Playlists
.empty());
934 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
935 auto move_fun
= boost::bind(&MPD::Connection::PlaylistMove
, _1
, playlist
, _2
, _3
);
936 moveSelectedItemsDown(myPlaylistEditor
->Content
, move_fun
);
940 bool MoveSelectedItemsTo::canBeRun() const
942 return myScreen
== myPlaylist
943 || myScreen
->isActiveWindow(myPlaylistEditor
->Content
);
946 void MoveSelectedItemsTo::run()
948 if (myScreen
== myPlaylist
)
950 if (!myPlaylist
->main().empty())
951 moveSelectedItemsTo(myPlaylist
->main(), boost::bind(&MPD::Connection::Move
, _1
, _2
, _3
));
955 assert(!myPlaylistEditor
->Playlists
.empty());
956 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
957 auto move_fun
= boost::bind(&MPD::Connection::PlaylistMove
, _1
, playlist
, _2
, _3
);
958 moveSelectedItemsTo(myPlaylistEditor
->Content
, move_fun
);
962 bool Add::canBeRun() const
964 return myScreen
!= myPlaylistEditor
965 || !myPlaylistEditor
->Playlists
.empty();
970 using Global::wFooter
;
973 Statusbar::put() << (myScreen
== myPlaylistEditor
? "Add to playlist: " : "Add: ");
974 std::string path
= wFooter
->getString();
978 Statusbar::put() << "Adding...";
980 if (myScreen
== myPlaylistEditor
)
981 Mpd
.AddToPlaylist(myPlaylistEditor
->Playlists
.current().value(), path
);
984 const char lastfm_url
[] = "lastfm://";
985 if (path
.compare(0, const_strlen(lastfm_url
), lastfm_url
) == 0
986 || path
.find(".asx", path
.length()-4) != std::string::npos
987 || path
.find(".cue", path
.length()-4) != std::string::npos
988 || path
.find(".m3u", path
.length()-4) != std::string::npos
989 || path
.find(".pls", path
.length()-4) != std::string::npos
990 || path
.find(".xspf", path
.length()-5) != std::string::npos
)
991 Mpd
.LoadPlaylist(path
);
998 bool SeekForward::canBeRun() const
1000 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
1003 void SeekForward::run()
1008 bool SeekBackward::canBeRun() const
1010 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
1013 void SeekBackward::run()
1018 bool ToggleDisplayMode::canBeRun() const
1020 return myScreen
== myPlaylist
1021 || myScreen
== myBrowser
1022 || myScreen
== mySearcher
1023 || myScreen
->isActiveWindow(myPlaylistEditor
->Content
);
1026 void ToggleDisplayMode::run()
1028 if (myScreen
== myPlaylist
)
1030 switch (Config
.playlist_display_mode
)
1032 case DisplayMode::Classic
:
1033 Config
.playlist_display_mode
= DisplayMode::Columns
;
1034 myPlaylist
->main().setItemDisplayer(boost::bind(
1035 Display::SongsInColumns
, _1
, myPlaylist
->proxySongList()
1037 if (Config
.titles_visibility
)
1038 myPlaylist
->main().setTitle(Display::Columns(myPlaylist
->main().getWidth()));
1040 myPlaylist
->main().setTitle("");
1042 case DisplayMode::Columns
:
1043 Config
.playlist_display_mode
= DisplayMode::Classic
;
1044 myPlaylist
->main().setItemDisplayer(boost::bind(
1045 Display::Songs
, _1
, myPlaylist
->proxySongList(), Config
.song_list_format
1047 myPlaylist
->main().setTitle("");
1049 Statusbar::printf("Playlist display mode: %1%", Config
.playlist_display_mode
);
1051 else if (myScreen
== myBrowser
)
1053 switch (Config
.browser_display_mode
)
1055 case DisplayMode::Classic
:
1056 Config
.browser_display_mode
= DisplayMode::Columns
;
1057 if (Config
.titles_visibility
)
1058 myBrowser
->main().setTitle(Display::Columns(myBrowser
->main().getWidth()));
1060 myBrowser
->main().setTitle("");
1062 case DisplayMode::Columns
:
1063 Config
.browser_display_mode
= DisplayMode::Classic
;
1064 myBrowser
->main().setTitle("");
1067 Statusbar::printf("Browser display mode: %1%", Config
.browser_display_mode
);
1069 else if (myScreen
== mySearcher
)
1071 switch (Config
.search_engine_display_mode
)
1073 case DisplayMode::Classic
:
1074 Config
.search_engine_display_mode
= DisplayMode::Columns
;
1076 case DisplayMode::Columns
:
1077 Config
.search_engine_display_mode
= DisplayMode::Classic
;
1080 Statusbar::printf("Search engine display mode: %1%", Config
.search_engine_display_mode
);
1081 if (mySearcher
->main().size() > SearchEngine::StaticOptions
)
1082 mySearcher
->main().setTitle(
1083 Config
.search_engine_display_mode
== DisplayMode::Columns
1084 && Config
.titles_visibility
1085 ? Display::Columns(mySearcher
->main().getWidth())
1089 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Content
))
1091 switch (Config
.playlist_editor_display_mode
)
1093 case DisplayMode::Classic
:
1094 Config
.playlist_editor_display_mode
= DisplayMode::Columns
;
1095 myPlaylistEditor
->Content
.setItemDisplayer(boost::bind(
1096 Display::SongsInColumns
, _1
, myPlaylistEditor
->contentProxyList()
1099 case DisplayMode::Columns
:
1100 Config
.playlist_editor_display_mode
= DisplayMode::Classic
;
1101 myPlaylistEditor
->Content
.setItemDisplayer(boost::bind(
1102 Display::Songs
, _1
, myPlaylistEditor
->contentProxyList(), Config
.song_list_format
1106 Statusbar::printf("Playlist editor display mode: %1%", Config
.playlist_editor_display_mode
);
1110 bool ToggleSeparatorsBetweenAlbums::canBeRun() const
1115 void ToggleSeparatorsBetweenAlbums::run()
1117 Config
.playlist_separate_albums
= !Config
.playlist_separate_albums
;
1118 Statusbar::printf("Separators between albums: %1%",
1119 Config
.playlist_separate_albums
? "on" : "off"
1123 #ifndef HAVE_CURL_CURL_H
1124 bool ToggleLyricsFetcher::canBeRun() const
1128 #endif // NOT HAVE_CURL_CURL_H
1130 void ToggleLyricsFetcher::run()
1132 # ifdef HAVE_CURL_CURL_H
1133 myLyrics
->ToggleFetcher();
1134 # endif // HAVE_CURL_CURL_H
1137 #ifndef HAVE_CURL_CURL_H
1138 bool ToggleFetchingLyricsInBackground::canBeRun() const
1142 #endif // NOT HAVE_CURL_CURL_H
1144 void ToggleFetchingLyricsInBackground::run()
1146 # ifdef HAVE_CURL_CURL_H
1147 Config
.fetch_lyrics_in_background
= !Config
.fetch_lyrics_in_background
;
1148 Statusbar::printf("Fetching lyrics for playing songs in background: %1%",
1149 Config
.fetch_lyrics_in_background
? "on" : "off"
1151 # endif // HAVE_CURL_CURL_H
1154 void TogglePlayingSongCentering::run()
1156 Config
.autocenter_mode
= !Config
.autocenter_mode
;
1157 Statusbar::printf("Centering playing song: %1%",
1158 Config
.autocenter_mode
? "on" : "off"
1160 if (Config
.autocenter_mode
&& !myPlaylist
->main().isFiltered())
1162 auto s
= myPlaylist
->nowPlayingSong();
1164 myPlaylist
->main().highlight(s
.getPosition());
1168 void UpdateDatabase::run()
1170 if (myScreen
== myBrowser
)
1171 Mpd
.UpdateDirectory(myBrowser
->CurrentDir());
1172 # ifdef HAVE_TAGLIB_H
1173 else if (myScreen
== myTagEditor
)
1174 Mpd
.UpdateDirectory(myTagEditor
->CurrentDir());
1175 # endif // HAVE_TAGLIB_H
1177 Mpd
.UpdateDirectory("/");
1180 bool JumpToPlayingSong::canBeRun() const
1182 return (myScreen
== myPlaylist
&& !myPlaylist
->isFiltered())
1183 || myScreen
== myBrowser
1184 || myScreen
== myLibrary
;
1187 void JumpToPlayingSong::run()
1189 auto s
= myPlaylist
->nowPlayingSong();
1192 if (myScreen
== myPlaylist
)
1194 myPlaylist
->main().highlight(s
.getPosition());
1196 else if (myScreen
== myBrowser
)
1198 myBrowser
->LocateSong(s
);
1201 else if (myScreen
== myLibrary
)
1203 myLibrary
->LocateSong(s
);
1207 void ToggleRepeat::run()
1209 Mpd
.SetRepeat(!Status::State::repeat());
1217 void ToggleRandom::run()
1219 Mpd
.SetRandom(!Status::State::random());
1222 bool StartSearching::canBeRun() const
1224 return myScreen
== mySearcher
&& !mySearcher
->main()[0].isInactive();
1227 void StartSearching::run()
1229 mySearcher
->main().highlight(SearchEngine::SearchButton
);
1230 mySearcher
->main().setHighlighting(0);
1231 mySearcher
->main().refresh();
1232 mySearcher
->main().setHighlighting(1);
1233 mySearcher
->enterPressed();
1236 bool SaveTagChanges::canBeRun() const
1238 # ifdef HAVE_TAGLIB_H
1239 return myScreen
== myTinyTagEditor
1240 || myScreen
->activeWindow() == myTagEditor
->TagTypes
;
1243 # endif // HAVE_TAGLIB_H
1246 void SaveTagChanges::run()
1248 # ifdef HAVE_TAGLIB_H
1249 if (myScreen
== myTinyTagEditor
)
1251 myTinyTagEditor
->main().highlight(myTinyTagEditor
->main().size()-2); // Save
1252 myTinyTagEditor
->enterPressed();
1254 else if (myScreen
->activeWindow() == myTagEditor
->TagTypes
)
1256 myTagEditor
->TagTypes
->highlight(myTagEditor
->TagTypes
->size()-1); // Save
1257 myTagEditor
->enterPressed();
1259 # endif // HAVE_TAGLIB_H
1262 void ToggleSingle::run()
1264 Mpd
.SetSingle(!Status::State::single());
1267 void ToggleConsume::run()
1269 Mpd
.SetConsume(!Status::State::consume());
1272 void ToggleCrossfade::run()
1274 Mpd
.SetCrossfade(Status::State::crossfade() ? 0 : Config
.crossfade_time
);
1277 void SetCrossfade::run()
1279 using Global::wFooter
;
1282 Statusbar::put() << "Set crossfade to: ";
1283 std::string crossfade
= wFooter
->getString();
1284 Statusbar::unlock();
1285 int cf
= fromString
<unsigned>(crossfade
);
1286 lowerBoundCheck(cf
, 1);
1287 Config
.crossfade_time
= cf
;
1288 Mpd
.SetCrossfade(cf
);
1291 void SetVolume::run()
1293 using Global::wFooter
;
1296 Statusbar::put() << "Set volume to: ";
1297 std::string strvolume
= wFooter
->getString();
1298 Statusbar::unlock();
1299 int volume
= fromString
<unsigned>(strvolume
);
1300 boundsCheck(volume
, 0, 100);
1301 Mpd
.SetVolume(volume
);
1302 Statusbar::printf("Volume set to %1%%%", volume
);
1305 bool EditSong::canBeRun() const
1307 # ifdef HAVE_TAGLIB_H
1308 return currentSong(myScreen
)
1309 && isMPDMusicDirSet();
1312 # endif // HAVE_TAGLIB_H
1315 void EditSong::run()
1317 # ifdef HAVE_TAGLIB_H
1318 auto s
= currentSong(myScreen
);
1319 myTinyTagEditor
->SetEdited(*s
);
1320 myTinyTagEditor
->switchTo();
1321 # endif // HAVE_TAGLIB_H
1324 bool EditLibraryTag::canBeRun() const
1326 # ifdef HAVE_TAGLIB_H
1327 return myScreen
->isActiveWindow(myLibrary
->Tags
)
1328 && !myLibrary
->Tags
.empty()
1329 && isMPDMusicDirSet();
1332 # endif // HAVE_TAGLIB_H
1335 void EditLibraryTag::run()
1337 # ifdef HAVE_TAGLIB_H
1338 using Global::wFooter
;
1341 Statusbar::put() << NC::Format::Bold
<< tagTypeToString(Config
.media_lib_primary_tag
) << NC::Format::NoBold
<< ": ";
1342 std::string new_tag
= wFooter
->getString(myLibrary
->Tags
.current().value().tag());
1343 Statusbar::unlock();
1344 if (!new_tag
.empty() && new_tag
!= myLibrary
->Tags
.current().value().tag())
1346 Statusbar::print("Updating tags...");
1348 Mpd
.AddSearch(Config
.media_lib_primary_tag
, myLibrary
->Tags
.current().value().tag());
1349 MPD::MutableSong::SetFunction set
= tagTypeToSetFunction(Config
.media_lib_primary_tag
);
1351 bool success
= true;
1352 std::string dir_to_update
;
1353 Mpd
.CommitSearchSongs([set
, &dir_to_update
, &new_tag
, &success
](MPD::Song s
) {
1356 MPD::MutableSong ms
= s
;
1357 ms
.setTags(set
, new_tag
, Config
.tags_separator
);
1358 Statusbar::printf("Updating tags in \"%1%\"...", ms
.getName());
1359 std::string path
= Config
.mpd_music_dir
+ ms
.getURI();
1360 if (!Tags::write(ms
))
1362 const char msg
[] = "Error while updating tags in \"%1%\"";
1363 Statusbar::printf(msg
, wideShorten(ms
.getURI(), COLS
-const_strlen(msg
)));
1366 if (dir_to_update
.empty())
1367 dir_to_update
= s
.getURI();
1369 dir_to_update
= getSharedDirectory(dir_to_update
, s
.getURI());
1373 Mpd
.UpdateDirectory(dir_to_update
);
1374 Statusbar::print("Tags updated successfully");
1377 # endif // HAVE_TAGLIB_H
1380 bool EditLibraryAlbum::canBeRun() const
1382 # ifdef HAVE_TAGLIB_H
1383 return myScreen
->isActiveWindow(myLibrary
->Albums
)
1384 && !myLibrary
->Albums
.empty()
1385 && isMPDMusicDirSet();
1388 # endif // HAVE_TAGLIB_H
1391 void EditLibraryAlbum::run()
1393 # ifdef HAVE_TAGLIB_H
1394 using Global::wFooter
;
1397 Statusbar::put() << NC::Format::Bold
<< "Album: " << NC::Format::NoBold
;
1398 std::string new_album
= wFooter
->getString(myLibrary
->Albums
.current().value().entry().album());
1399 Statusbar::unlock();
1400 if (!new_album
.empty() && new_album
!= myLibrary
->Albums
.current().value().entry().album())
1403 Statusbar::print("Updating tags...");
1404 for (size_t i
= 0; i
< myLibrary
->Songs
.size(); ++i
)
1406 Statusbar::printf("Updating tags in \"%1%\"...", myLibrary
->Songs
[i
].value().getName());
1407 std::string path
= Config
.mpd_music_dir
+ myLibrary
->Songs
[i
].value().getURI();
1408 TagLib::FileRef
f(path
.c_str());
1411 const char msg
[] = "Error while opening file \"%1%\"";
1412 Statusbar::printf(msg
, wideShorten(myLibrary
->Songs
[i
].value().getURI(), COLS
-const_strlen(msg
)));
1416 f
.tag()->setAlbum(ToWString(new_album
));
1419 const char msg
[] = "Error while writing tags in \"%1%\"";
1420 Statusbar::printf(msg
, wideShorten(myLibrary
->Songs
[i
].value().getURI(), COLS
-const_strlen(msg
)));
1427 Mpd
.UpdateDirectory(getSharedDirectory(myLibrary
->Songs
.beginV(), myLibrary
->Songs
.endV()));
1428 Statusbar::print("Tags updated successfully");
1431 # endif // HAVE_TAGLIB_H
1434 bool EditDirectoryName::canBeRun() const
1436 return ((myScreen
== myBrowser
1437 && !myBrowser
->main().empty()
1438 && myBrowser
->main().current().value().type
== MPD::itDirectory
)
1439 # ifdef HAVE_TAGLIB_H
1440 || (myScreen
->activeWindow() == myTagEditor
->Dirs
1441 && !myTagEditor
->Dirs
->empty()
1442 && myTagEditor
->Dirs
->choice() > 0)
1443 # endif // HAVE_TAGLIB_H
1444 ) && isMPDMusicDirSet();
1447 void EditDirectoryName::run()
1449 using Global::wFooter
;
1451 if (myScreen
== myBrowser
)
1453 std::string old_dir
= myBrowser
->main().current().value().name
;
1455 Statusbar::put() << NC::Format::Bold
<< "Directory: " << NC::Format::NoBold
;
1456 std::string new_dir
= wFooter
->getString(old_dir
);
1457 Statusbar::unlock();
1458 if (!new_dir
.empty() && new_dir
!= old_dir
)
1460 std::string full_old_dir
;
1461 if (!myBrowser
->isLocal())
1462 full_old_dir
+= Config
.mpd_music_dir
;
1463 full_old_dir
+= old_dir
;
1464 std::string full_new_dir
;
1465 if (!myBrowser
->isLocal())
1466 full_new_dir
+= Config
.mpd_music_dir
;
1467 full_new_dir
+= new_dir
;
1468 int rename_result
= rename(full_old_dir
.c_str(), full_new_dir
.c_str());
1469 if (rename_result
== 0)
1471 const char msg
[] = "Directory renamed to \"%1%\"";
1472 Statusbar::printf(msg
, wideShorten(new_dir
, COLS
-const_strlen(msg
)));
1473 if (!myBrowser
->isLocal())
1474 Mpd
.UpdateDirectory(getSharedDirectory(old_dir
, new_dir
));
1475 myBrowser
->GetDirectory(myBrowser
->CurrentDir());
1479 const char msg
[] = "Couldn't rename \"%1%\": %s";
1480 Statusbar::printf(msg
, wideShorten(old_dir
, COLS
-const_strlen(msg
)-25), strerror(errno
));
1484 # ifdef HAVE_TAGLIB_H
1485 else if (myScreen
->activeWindow() == myTagEditor
->Dirs
)
1487 std::string old_dir
= myTagEditor
->Dirs
->current().value().first
;
1489 Statusbar::put() << NC::Format::Bold
<< "Directory: " << NC::Format::NoBold
;
1490 std::string new_dir
= wFooter
->getString(old_dir
);
1491 Statusbar::unlock();
1492 if (!new_dir
.empty() && new_dir
!= old_dir
)
1494 std::string full_old_dir
= Config
.mpd_music_dir
+ myTagEditor
->CurrentDir() + "/" + old_dir
;
1495 std::string full_new_dir
= Config
.mpd_music_dir
+ myTagEditor
->CurrentDir() + "/" + new_dir
;
1496 if (rename(full_old_dir
.c_str(), full_new_dir
.c_str()) == 0)
1498 const char msg
[] = "Directory renamed to \"%1%\"";
1499 Statusbar::printf(msg
, wideShorten(new_dir
, COLS
-const_strlen(msg
)));
1500 Mpd
.UpdateDirectory(myTagEditor
->CurrentDir());
1504 const char msg
[] = "Couldn't rename \"%1%\": %2%";
1505 Statusbar::printf(msg
, wideShorten(old_dir
, COLS
-const_strlen(msg
)-25), strerror(errno
));
1509 # endif // HAVE_TAGLIB_H
1512 bool EditPlaylistName::canBeRun() const
1514 return (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
)
1515 && !myPlaylistEditor
->Playlists
.empty())
1516 || (myScreen
== myBrowser
1517 && !myBrowser
->main().empty()
1518 && myBrowser
->main().current().value().type
== MPD::itPlaylist
);
1521 void EditPlaylistName::run()
1523 using Global::wFooter
;
1525 std::string old_name
;
1526 if (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
))
1527 old_name
= myPlaylistEditor
->Playlists
.current().value();
1529 old_name
= myBrowser
->main().current().value().name
;
1531 Statusbar::put() << NC::Format::Bold
<< "Playlist: " << NC::Format::NoBold
;
1532 std::string new_name
= wFooter
->getString(old_name
);
1533 Statusbar::unlock();
1534 if (!new_name
.empty() && new_name
!= old_name
)
1536 Mpd
.Rename(old_name
, new_name
);
1537 const char msg
[] = "Playlist renamed to \"%1%\"";
1538 Statusbar::printf(msg
, wideShorten(new_name
, COLS
-const_strlen(msg
)));
1539 if (!myBrowser
->isLocal())
1540 myBrowser
->GetDirectory("/");
1544 bool EditLyrics::canBeRun() const
1546 return myScreen
== myLyrics
;
1549 void EditLyrics::run()
1554 bool JumpToBrowser::canBeRun() const
1556 return currentSong(myScreen
);
1559 void JumpToBrowser::run()
1561 auto s
= currentSong(myScreen
);
1562 myBrowser
->LocateSong(*s
);
1565 bool JumpToMediaLibrary::canBeRun() const
1567 return currentSong(myScreen
);
1570 void JumpToMediaLibrary::run()
1572 auto s
= currentSong(myScreen
);
1573 myLibrary
->LocateSong(*s
);
1576 bool JumpToPlaylistEditor::canBeRun() const
1578 return myScreen
== myBrowser
1579 && myBrowser
->main().current().value().type
== MPD::itPlaylist
;
1582 void JumpToPlaylistEditor::run()
1584 myPlaylistEditor
->Locate(myBrowser
->main().current().value().name
);
1587 void ToggleScreenLock::run()
1589 using Global::wFooter
;
1590 using Global::myLockedScreen
;
1592 if (myLockedScreen
!= 0)
1594 BaseScreen::unlock();
1595 Actions::setResizeFlags();
1597 Statusbar::print("Screen unlocked");
1601 int part
= Config
.locked_screen_width_part
*100;
1602 if (Config
.ask_for_locked_screen_width_part
)
1605 Statusbar::put() << "% of the locked screen's width to be reserved (20-80): ";
1606 std::string strpart
= wFooter
->getString(boost::lexical_cast
<std::string
>(part
));
1607 Statusbar::unlock();
1608 part
= fromString
<unsigned>(strpart
);
1610 boundsCheck(part
, 20, 80);
1611 Config
.locked_screen_width_part
= part
/100.0;
1612 if (myScreen
->lock())
1613 Statusbar::printf("Screen locked (with %1%%% width)", part
);
1615 Statusbar::print("Current screen can't be locked");
1619 bool JumpToTagEditor::canBeRun() const
1621 # ifdef HAVE_TAGLIB_H
1622 return currentSong(myScreen
)
1623 && isMPDMusicDirSet();
1626 # endif // HAVE_TAGLIB_H
1629 void JumpToTagEditor::run()
1631 # ifdef HAVE_TAGLIB_H
1632 auto s
= currentSong(myScreen
);
1633 myTagEditor
->LocateSong(*s
);
1634 # endif // HAVE_TAGLIB_H
1637 bool JumpToPositionInSong::canBeRun() const
1639 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
1642 void JumpToPositionInSong::run()
1644 using Global::wFooter
;
1646 const MPD::Song s
= myPlaylist
->nowPlayingSong();
1649 Statusbar::put() << "Position to go (in %/m:ss/seconds(s)): ";
1650 std::string strpos
= wFooter
->getString();
1651 Statusbar::unlock();
1656 if (boost::regex_match(strpos
, what
, rx
.assign("([0-9]+):([0-9]{2})"))) // mm:ss
1658 int mins
= fromString
<int>(what
[1]);
1659 int secs
= fromString
<int>(what
[2]);
1660 boundsCheck(secs
, 0, 60);
1661 Mpd
.Seek(s
.getPosition(), mins
* 60 + secs
);
1663 else if (boost::regex_match(strpos
, what
, rx
.assign("([0-9]+)s"))) // position in seconds
1665 int secs
= fromString
<int>(what
[1]);
1666 Mpd
.Seek(s
.getPosition(), secs
);
1668 else if (boost::regex_match(strpos
, what
, rx
.assign("([0-9]+)[%]{0,1}"))) // position in %
1670 int percent
= fromString
<int>(what
[1]);
1671 boundsCheck(percent
, 0, 100);
1672 int secs
= (percent
* s
.getDuration()) / 100.0;
1673 Mpd
.Seek(s
.getPosition(), secs
);
1676 Statusbar::print("Invalid format ([m]:[ss], [s]s, [%]%, [%] accepted)");
1679 bool ReverseSelection::canBeRun() const
1681 auto w
= hasSongs(myScreen
);
1682 return w
&& w
->allowsSelection();
1685 void ReverseSelection::run()
1687 auto w
= hasSongs(myScreen
);
1688 w
->reverseSelection();
1689 Statusbar::print("Selection reversed");
1692 bool RemoveSelection::canBeRun() const
1694 return proxySongList(myScreen
);
1697 void RemoveSelection::run()
1699 auto pl
= proxySongList(myScreen
);
1700 for (size_t i
= 0; i
< pl
.size(); ++i
)
1701 pl
.setSelected(i
, false);
1702 Statusbar::print("Selection removed");
1705 bool SelectAlbum::canBeRun() const
1707 auto w
= hasSongs(myScreen
);
1708 return w
&& w
->allowsSelection() && w
->proxySongList();
1711 void SelectAlbum::run()
1713 auto pl
= proxySongList(myScreen
);
1717 size_t pos
= pl
.choice();
1718 if (MPD::Song
*s
= pl
.getSong(pos
))
1720 std::string album
= s
->getAlbum();
1721 // select song under cursor
1722 pl
.setSelected(pos
, true);
1726 s
= pl
.getSong(--pos
);
1727 if (!s
|| s
->getAlbum() != album
)
1730 pl
.setSelected(pos
, true);
1734 while (pos
< pl
.size() - 1)
1736 s
= pl
.getSong(++pos
);
1737 if (!s
|| s
->getAlbum() != album
)
1740 pl
.setSelected(pos
, true);
1742 Statusbar::print("Album around cursor position selected");
1746 bool AddSelectedItems::canBeRun() const
1748 return myScreen
!= mySelectedItemsAdder
;
1751 void AddSelectedItems::run()
1753 mySelectedItemsAdder
->switchTo();
1756 void CropMainPlaylist::run()
1758 auto &w
= myPlaylist
->main();
1759 // cropping doesn't make sense in this case
1763 if (Config
.ask_before_clearing_playlists
)
1764 yes
= askYesNoQuestion("Do you really want to crop main playlist?", Status::trace
);
1767 Statusbar::print("Cropping playlist...");
1768 selectCurrentIfNoneSelected(w
);
1769 cropPlaylist(w
, boost::bind(&MPD::Connection::Delete
, _1
, _2
));
1770 Statusbar::print("Playlist cropped");
1774 bool CropPlaylist::canBeRun() const
1776 return myScreen
== myPlaylistEditor
;
1779 void CropPlaylist::run()
1781 auto &w
= myPlaylistEditor
->Content
;
1782 // cropping doesn't make sense in this case
1785 assert(!myPlaylistEditor
->Playlists
.empty());
1786 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
1788 if (Config
.ask_before_clearing_playlists
)
1789 yes
= askYesNoQuestion(
1790 boost::format("Do you really want to crop playlist \"%1%\"?") % playlist
,
1795 selectCurrentIfNoneSelected(w
);
1796 Statusbar::printf("Cropping playlist \"%1%\"...", playlist
);
1797 cropPlaylist(w
, boost::bind(&MPD::Connection::PlaylistDelete
, _1
, playlist
, _2
));
1798 Statusbar::printf("Playlist \"%1%\" cropped", playlist
);
1802 void ClearMainPlaylist::run()
1805 if (Config
.ask_before_clearing_playlists
)
1806 yes
= askYesNoQuestion("Do you really want to clear main playlist?", Status::trace
);
1809 auto delete_fun
= boost::bind(&MPD::Connection::Delete
, _1
, _2
);
1810 auto clear_fun
= boost::bind(&MPD::Connection::ClearMainPlaylist
, _1
);
1811 Statusbar::printf("Deleting items...");
1812 clearPlaylist(myPlaylist
->main(), delete_fun
, clear_fun
);
1813 Statusbar::printf("Items deleted");
1814 myPlaylist
->main().reset();
1818 bool ClearPlaylist::canBeRun() const
1820 return myScreen
== myPlaylistEditor
;
1823 void ClearPlaylist::run()
1825 if (myPlaylistEditor
->Playlists
.empty())
1827 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
1829 if (Config
.ask_before_clearing_playlists
)
1830 yes
= askYesNoQuestion(
1831 boost::format("Do you really want to clear playlist \"%1%\"?") % playlist
,
1836 auto delete_fun
= boost::bind(&MPD::Connection::PlaylistDelete
, _1
, playlist
, _2
);
1837 auto clear_fun
= boost::bind(&MPD::Connection::ClearPlaylist
, _1
, playlist
);
1838 Statusbar::printf("Deleting items from \"%1%\"...", playlist
);
1839 clearPlaylist(myPlaylistEditor
->Content
, delete_fun
, clear_fun
);
1840 Statusbar::printf("Items deleted from \"%1%\"", playlist
);
1844 bool SortPlaylist::canBeRun() const
1846 return myScreen
== myPlaylist
;
1849 void SortPlaylist::run()
1851 mySortPlaylistDialog
->switchTo();
1854 bool ReversePlaylist::canBeRun() const
1856 return myScreen
== myPlaylist
;
1859 void ReversePlaylist::run()
1861 myPlaylist
->Reverse();
1864 bool ApplyFilter::canBeRun() const
1866 auto w
= dynamic_cast<Filterable
*>(myScreen
);
1867 return w
&& w
->allowsFiltering();
1870 void ApplyFilter::run()
1872 using Global::wFooter
;
1874 Filterable
*f
= dynamic_cast<Filterable
*>(myScreen
);
1875 std::string filter
= f
->currentFilter();
1876 // if filter is already here, apply it
1877 if (!filter
.empty())
1879 f
->applyFilter(filter
);
1880 myScreen
->refreshWindow();
1884 Statusbar::put() << NC::Format::Bold
<< "Apply filter: " << NC::Format::NoBold
;
1885 wFooter
->setGetStringHelper(Statusbar::Helpers::ApplyFilterImmediately(f
, filter
));
1886 wFooter
->getString(filter
);
1887 wFooter
->setGetStringHelper(Statusbar::Helpers::getString
);
1888 Statusbar::unlock();
1890 filter
= f
->currentFilter();
1893 myPlaylist
->main().clearFilterResults();
1894 Statusbar::printf("Filtering disabled");
1898 // apply filter here so even if old one wasn't modified
1899 // (and callback wasn't invoked), it still gets applied.
1900 f
->applyFilter(filter
);
1901 Statusbar::printf("Using filter \"%1%\"", filter
);
1904 if (myScreen
== myPlaylist
)
1906 myPlaylist
->EnableHighlighting();
1907 myPlaylist
->reloadTotalLength();
1910 listsChangeFinisher();
1913 bool Find::canBeRun() const
1915 return myScreen
== myHelp
1916 || myScreen
== myLyrics
1917 # ifdef HAVE_CURL_CURL_H
1918 || myScreen
== myLastfm
1919 # endif // HAVE_CURL_CURL_H
1925 using Global::wFooter
;
1928 Statusbar::put() << "Find: ";
1929 std::string findme
= wFooter
->getString();
1930 Statusbar::unlock();
1932 Statusbar::print("Searching...");
1933 auto s
= static_cast<Screen
<NC::Scrollpad
> *>(myScreen
);
1934 s
->main().removeProperties();
1935 if (findme
.empty() || s
->main().setProperties(NC::Format::Reverse
, findme
, NC::Format::NoReverse
))
1936 Statusbar::print("Done");
1938 Statusbar::print("No matching patterns found");
1942 bool FindItemBackward::canBeRun() const
1944 auto w
= dynamic_cast<Searchable
*>(myScreen
);
1945 return w
&& w
->allowsSearching();
1948 void FindItemForward::run()
1950 findItem(::Find::Forward
);
1951 listsChangeFinisher();
1954 bool FindItemForward::canBeRun() const
1956 auto w
= dynamic_cast<Searchable
*>(myScreen
);
1957 return w
&& w
->allowsSearching();
1960 void FindItemBackward::run()
1962 findItem(::Find::Backward
);
1963 listsChangeFinisher();
1966 bool NextFoundItem::canBeRun() const
1968 return dynamic_cast<Searchable
*>(myScreen
);
1971 void NextFoundItem::run()
1973 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
1974 w
->nextFound(Config
.wrapped_search
);
1975 listsChangeFinisher();
1978 bool PreviousFoundItem::canBeRun() const
1980 return dynamic_cast<Searchable
*>(myScreen
);
1983 void PreviousFoundItem::run()
1985 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
1986 w
->prevFound(Config
.wrapped_search
);
1987 listsChangeFinisher();
1990 void ToggleFindMode::run()
1992 Config
.wrapped_search
= !Config
.wrapped_search
;
1993 Statusbar::printf("Search mode: %1%",
1994 Config
.wrapped_search
? "Wrapped" : "Normal"
1998 void ToggleReplayGainMode::run()
2000 using Global::wFooter
;
2003 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]";
2009 answer
= wFooter
->readKey();
2011 while (answer
!= 'o' && answer
!= 't' && answer
!= 'a');
2012 Statusbar::unlock();
2013 Mpd
.SetReplayGainMode(answer
== 't' ? MPD::rgmTrack
: (answer
== 'a' ? MPD::rgmAlbum
: MPD::rgmOff
));
2014 Statusbar::printf("Replay gain mode: %1%", Mpd
.GetReplayGainMode());
2017 void ToggleSpaceMode::run()
2019 Config
.space_selects
= !Config
.space_selects
;
2020 Statusbar::printf("Space mode: %1% item", Config
.space_selects
? "select" : "add");
2023 void ToggleAddMode::run()
2025 std::string mode_desc
;
2026 switch (Config
.space_add_mode
)
2028 case SpaceAddMode::AddRemove
:
2029 Config
.space_add_mode
= SpaceAddMode::AlwaysAdd
;
2030 mode_desc
= "always add an item to playlist";
2032 case SpaceAddMode::AlwaysAdd
:
2033 Config
.space_add_mode
= SpaceAddMode::AddRemove
;
2034 mode_desc
= "add an item to playlist or remove if already added";
2037 Statusbar::printf("Add mode: %1%", mode_desc
);
2040 void ToggleMouse::run()
2042 Config
.mouse_support
= !Config
.mouse_support
;
2043 mousemask(Config
.mouse_support
? ALL_MOUSE_EVENTS
: 0, 0);
2044 Statusbar::printf("Mouse support %1%",
2045 Config
.mouse_support
? "enabled" : "disabled"
2049 void ToggleBitrateVisibility::run()
2051 Config
.display_bitrate
= !Config
.display_bitrate
;
2052 Statusbar::printf("Bitrate visibility %1%",
2053 Config
.display_bitrate
? "enabled" : "disabled"
2057 void AddRandomItems::run()
2059 using Global::wFooter
;
2062 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] ";
2068 answer
= wFooter
->readKey();
2070 while (answer
!= 's' && answer
!= 'a' && answer
!= 'b');
2071 Statusbar::unlock();
2073 mpd_tag_type tag_type
= MPD_TAG_ARTIST
;
2074 std::string tag_type_str
;
2077 tag_type
= charToTagType(answer
);
2078 tag_type_str
= boost::locale::to_lower(tagTypeToString(tag_type
));
2081 tag_type_str
= "song";
2084 Statusbar::put() << "Number of random " << tag_type_str
<< "s: ";
2085 std::string strnum
= wFooter
->getString();
2086 Statusbar::unlock();
2087 size_t number
= fromString
<size_t>(strnum
);
2088 if (number
&& (answer
== 's' ? Mpd
.AddRandomSongs(number
) : Mpd
.AddRandomTag(tag_type
, number
)))
2090 Statusbar::printf("%1% random %2%%3% added to playlist",
2091 number
, tag_type_str
, number
== 1 ? "" : "s"
2096 bool ToggleBrowserSortMode::canBeRun() const
2098 return myScreen
== myBrowser
;
2101 void ToggleBrowserSortMode::run()
2103 switch (Config
.browser_sort_mode
)
2105 case SortMode::Name
:
2106 Config
.browser_sort_mode
= SortMode::ModificationTime
;
2107 Statusbar::print("Sort songs by: modification time");
2109 case SortMode::ModificationTime
:
2110 Config
.browser_sort_mode
= SortMode::CustomFormat
;
2111 Statusbar::print("Sort songs by: custom format");
2113 case SortMode::CustomFormat
:
2114 Config
.browser_sort_mode
= SortMode::NoOp
;
2115 Statusbar::print("Do not sort songs");
2117 case SortMode::NoOp
:
2118 Config
.browser_sort_mode
= SortMode::Name
;
2119 Statusbar::print("Sort songs by: name");
2121 withUnfilteredMenuReapplyFilter(myBrowser
->main(), [] {
2122 if (Config
.browser_sort_mode
!= SortMode::NoOp
)
2123 std::sort(myBrowser
->main().begin()+(myBrowser
->CurrentDir() != "/"), myBrowser
->main().end(),
2124 LocaleBasedItemSorting(std::locale(), Config
.ignore_leading_the
, Config
.browser_sort_mode
)
2129 bool ToggleLibraryTagType::canBeRun() const
2131 return (myScreen
->isActiveWindow(myLibrary
->Tags
))
2132 || (myLibrary
->Columns() == 2 && myScreen
->isActiveWindow(myLibrary
->Albums
));
2135 void ToggleLibraryTagType::run()
2137 using Global::wFooter
;
2140 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] ";
2146 answer
= wFooter
->readKey();
2148 while (answer
!= 'a' && answer
!= 'A' && answer
!= 'y' && answer
!= 'g' && answer
!= 'c' && answer
!= 'p');
2149 Statusbar::unlock();
2150 mpd_tag_type new_tagitem
= charToTagType(answer
);
2151 if (new_tagitem
!= Config
.media_lib_primary_tag
)
2153 Config
.media_lib_primary_tag
= new_tagitem
;
2154 std::string item_type
= tagTypeToString(Config
.media_lib_primary_tag
);
2155 myLibrary
->Tags
.setTitle(Config
.titles_visibility
? item_type
+ "s" : "");
2156 myLibrary
->Tags
.reset();
2157 item_type
= boost::locale::to_lower(item_type
);
2158 std::string and_mtime
= Config
.media_library_sort_by_mtime
?
2161 if (myLibrary
->Columns() == 2)
2163 myLibrary
->Songs
.clear();
2164 myLibrary
->Albums
.reset();
2165 myLibrary
->Albums
.clear();
2166 myLibrary
->Albums
.setTitle(Config
.titles_visibility
? "Albums (sorted by " + item_type
+ and_mtime
+ ")" : "");
2167 myLibrary
->Albums
.display();
2171 myLibrary
->Tags
.clear();
2172 myLibrary
->Tags
.display();
2174 Statusbar::printf("Switched to the list of %1%s", item_type
);
2178 bool ToggleMediaLibrarySortMode::canBeRun() const
2180 return myScreen
== myLibrary
;
2183 void ToggleMediaLibrarySortMode::run()
2185 myLibrary
->toggleSortMode();
2188 bool RefetchLyrics::canBeRun() const
2190 # ifdef HAVE_CURL_CURL_H
2191 return myScreen
== myLyrics
;
2194 # endif // HAVE_CURL_CURL_H
2197 void RefetchLyrics::run()
2199 # ifdef HAVE_CURL_CURL_H
2200 myLyrics
->Refetch();
2201 # endif // HAVE_CURL_CURL_H
2204 bool SetSelectedItemsPriority::canBeRun() const
2206 if (Mpd
.Version() < 17)
2208 Statusbar::print("Priorities are supported in MPD >= 0.17.0");
2211 return myScreen
== myPlaylist
&& !myPlaylist
->main().empty();
2214 void SetSelectedItemsPriority::run()
2216 using Global::wFooter
;
2219 Statusbar::put() << "Set priority [0-255]: ";
2220 std::string strprio
= wFooter
->getString();
2221 Statusbar::unlock();
2222 unsigned prio
= fromString
<unsigned>(strprio
);
2223 boundsCheck(prio
, 0u, 255u);
2224 myPlaylist
->SetSelectedItemsPriority(prio
);
2227 bool SetVisualizerSampleMultiplier::canBeRun() const
2229 # ifdef ENABLE_VISUALIZER
2230 return myScreen
== myVisualizer
;
2233 # endif // ENABLE_VISUALIZER
2236 void SetVisualizerSampleMultiplier::run()
2238 # ifdef ENABLE_VISUALIZER
2239 using Global::wFooter
;
2242 Statusbar::put() << "Set visualizer sample multiplier: ";
2243 std::string smultiplier
= wFooter
->getString();
2244 Statusbar::unlock();
2246 double multiplier
= fromString
<double>(smultiplier
);
2247 lowerBoundCheck(multiplier
, 0.0);
2248 Config
.visualizer_sample_multiplier
= multiplier
;
2249 # endif // ENABLE_VISUALIZER
2252 bool FilterPlaylistOnPriorities::canBeRun() const
2254 return myScreen
== myPlaylist
;
2257 void FilterPlaylistOnPriorities::run()
2259 using Global::wFooter
;
2262 Statusbar::put() << "Show songs with priority higher than: ";
2263 std::string strprio
= wFooter
->getString();
2264 Statusbar::unlock();
2265 unsigned prio
= fromString
<unsigned>(strprio
);
2266 boundsCheck(prio
, 0u, 255u);
2267 myPlaylist
->main().filter(myPlaylist
->main().begin(), myPlaylist
->main().end(),
2268 [prio
](const NC::Menu
<MPD::Song
>::Item
&s
) {
2269 return s
.value().getPrio() > prio
;
2271 Statusbar::printf("Playlist filtered (songs with priority higher than %1%)", prio
);
2274 void ShowSongInfo::run()
2276 mySongInfo
->switchTo();
2279 bool ShowArtistInfo::canBeRun() const
2281 #ifdef HAVE_CURL_CURL_H
2282 return myScreen
== myLastfm
2283 || (myScreen
->isActiveWindow(myLibrary
->Tags
)
2284 && !myLibrary
->Tags
.empty()
2285 && Config
.media_lib_primary_tag
== MPD_TAG_ARTIST
)
2286 || currentSong(myScreen
);
2289 # endif // NOT HAVE_CURL_CURL_H
2292 void ShowArtistInfo::run()
2294 # ifdef HAVE_CURL_CURL_H
2295 if (myScreen
== myLastfm
)
2297 myLastfm
->switchTo();
2302 if (myScreen
->isActiveWindow(myLibrary
->Tags
))
2304 assert(!myLibrary
->Tags
.empty());
2305 assert(Config
.media_lib_primary_tag
== MPD_TAG_ARTIST
);
2306 artist
= myLibrary
->Tags
.current().value().tag();
2310 auto s
= currentSong(myScreen
);
2312 artist
= s
->getArtist();
2315 if (!artist
.empty())
2317 myLastfm
->queueJob(LastFm::ArtistInfo(artist
, Config
.lastfm_preferred_language
));
2318 myLastfm
->switchTo();
2320 # endif // HAVE_CURL_CURL_H
2323 void ShowLyrics::run()
2325 myLyrics
->switchTo();
2330 ExitMainLoop
= true;
2333 void NextScreen::run()
2335 if (Config
.screen_switcher_previous
)
2337 if (auto tababble
= dynamic_cast<Tabbable
*>(myScreen
))
2338 tababble
->switchToPreviousScreen();
2340 else if (!Config
.screen_sequence
.empty())
2342 const auto &seq
= Config
.screen_sequence
;
2343 auto screen_type
= std::find(seq
.begin(), seq
.end(), myScreen
->type());
2344 if (++screen_type
== seq
.end())
2345 toScreen(seq
.front())->switchTo();
2347 toScreen(*screen_type
)->switchTo();
2351 void PreviousScreen::run()
2353 if (Config
.screen_switcher_previous
)
2355 if (auto tababble
= dynamic_cast<Tabbable
*>(myScreen
))
2356 tababble
->switchToPreviousScreen();
2358 else if (!Config
.screen_sequence
.empty())
2360 const auto &seq
= Config
.screen_sequence
;
2361 auto screen_type
= std::find(seq
.begin(), seq
.end(), myScreen
->type());
2362 if (screen_type
== seq
.begin())
2363 toScreen(seq
.back())->switchTo();
2365 toScreen(*--screen_type
)->switchTo();
2369 bool ShowHelp::canBeRun() const
2371 return myScreen
!= myHelp
2372 # ifdef HAVE_TAGLIB_H
2373 && myScreen
!= myTinyTagEditor
2374 # endif // HAVE_TAGLIB_H
2378 void ShowHelp::run()
2383 bool ShowPlaylist::canBeRun() const
2385 return myScreen
!= myPlaylist
2386 # ifdef HAVE_TAGLIB_H
2387 && myScreen
!= myTinyTagEditor
2388 # endif // HAVE_TAGLIB_H
2392 void ShowPlaylist::run()
2394 myPlaylist
->switchTo();
2397 bool ShowBrowser::canBeRun() const
2399 return myScreen
!= myBrowser
2400 # ifdef HAVE_TAGLIB_H
2401 && myScreen
!= myTinyTagEditor
2402 # endif // HAVE_TAGLIB_H
2406 void ShowBrowser::run()
2408 myBrowser
->switchTo();
2411 bool ChangeBrowseMode::canBeRun() const
2413 return myScreen
== myBrowser
;
2416 void ChangeBrowseMode::run()
2418 myBrowser
->ChangeBrowseMode();
2421 bool ShowSearchEngine::canBeRun() const
2423 return myScreen
!= mySearcher
2424 # ifdef HAVE_TAGLIB_H
2425 && myScreen
!= myTinyTagEditor
2426 # endif // HAVE_TAGLIB_H
2430 void ShowSearchEngine::run()
2432 mySearcher
->switchTo();
2435 bool ResetSearchEngine::canBeRun() const
2437 return myScreen
== mySearcher
;
2440 void ResetSearchEngine::run()
2442 mySearcher
->reset();
2445 bool ShowMediaLibrary::canBeRun() const
2447 return myScreen
!= myLibrary
2448 # ifdef HAVE_TAGLIB_H
2449 && myScreen
!= myTinyTagEditor
2450 # endif // HAVE_TAGLIB_H
2454 void ShowMediaLibrary::run()
2456 myLibrary
->switchTo();
2459 bool ToggleMediaLibraryColumnsMode::canBeRun() const
2461 return myScreen
== myLibrary
;
2464 void ToggleMediaLibraryColumnsMode::run()
2466 myLibrary
->toggleColumnsMode();
2467 myLibrary
->refresh();
2470 bool ShowPlaylistEditor::canBeRun() const
2472 return myScreen
!= myPlaylistEditor
2473 # ifdef HAVE_TAGLIB_H
2474 && myScreen
!= myTinyTagEditor
2475 # endif // HAVE_TAGLIB_H
2479 void ShowPlaylistEditor::run()
2481 myPlaylistEditor
->switchTo();
2484 bool ShowTagEditor::canBeRun() const
2486 # ifdef HAVE_TAGLIB_H
2487 return myScreen
!= myTagEditor
2488 && myScreen
!= myTinyTagEditor
;
2491 # endif // HAVE_TAGLIB_H
2494 void ShowTagEditor::run()
2496 # ifdef HAVE_TAGLIB_H
2497 if (isMPDMusicDirSet())
2498 myTagEditor
->switchTo();
2499 # endif // HAVE_TAGLIB_H
2502 bool ShowOutputs::canBeRun() const
2504 # ifdef ENABLE_OUTPUTS
2505 return myScreen
!= myOutputs
2506 # ifdef HAVE_TAGLIB_H
2507 && myScreen
!= myTinyTagEditor
2508 # endif // HAVE_TAGLIB_H
2512 # endif // ENABLE_OUTPUTS
2515 void ShowOutputs::run()
2517 # ifdef ENABLE_OUTPUTS
2518 myOutputs
->switchTo();
2519 # endif // ENABLE_OUTPUTS
2522 bool ShowVisualizer::canBeRun() const
2524 # ifdef ENABLE_VISUALIZER
2525 return myScreen
!= myVisualizer
2526 # ifdef HAVE_TAGLIB_H
2527 && myScreen
!= myTinyTagEditor
2528 # endif // HAVE_TAGLIB_H
2532 # endif // ENABLE_VISUALIZER
2535 void ShowVisualizer::run()
2537 # ifdef ENABLE_VISUALIZER
2538 myVisualizer
->switchTo();
2539 # endif // ENABLE_VISUALIZER
2542 bool ShowClock::canBeRun() const
2544 # ifdef ENABLE_CLOCK
2545 return myScreen
!= myClock
2546 # ifdef HAVE_TAGLIB_H
2547 && myScreen
!= myTinyTagEditor
2548 # endif // HAVE_TAGLIB_H
2552 # endif // ENABLE_CLOCK
2555 void ShowClock::run()
2557 # ifdef ENABLE_CLOCK
2558 myClock
->switchTo();
2559 # endif // ENABLE_CLOCK
2562 #ifdef HAVE_TAGLIB_H
2563 bool ShowServerInfo::canBeRun() const
2565 return myScreen
!= myTinyTagEditor
;
2567 #endif // HAVE_TAGLIB_H
2569 void ShowServerInfo::run()
2571 myServerInfo
->switchTo();
2578 void populateActions()
2580 auto insert_action
= [](Actions::BaseAction
*a
) {
2581 AvailableActions
[static_cast<size_t>(a
->type())] = a
;
2583 insert_action(new Actions::Dummy());
2584 insert_action(new Actions::MouseEvent());
2585 insert_action(new Actions::ScrollUp());
2586 insert_action(new Actions::ScrollDown());
2587 insert_action(new Actions::ScrollUpArtist());
2588 insert_action(new Actions::ScrollUpAlbum());
2589 insert_action(new Actions::ScrollDownArtist());
2590 insert_action(new Actions::ScrollDownAlbum());
2591 insert_action(new Actions::PageUp());
2592 insert_action(new Actions::PageDown());
2593 insert_action(new Actions::MoveHome());
2594 insert_action(new Actions::MoveEnd());
2595 insert_action(new Actions::ToggleInterface());
2596 insert_action(new Actions::JumpToParentDirectory());
2597 insert_action(new Actions::PressEnter());
2598 insert_action(new Actions::PressSpace());
2599 insert_action(new Actions::PreviousColumn());
2600 insert_action(new Actions::NextColumn());
2601 insert_action(new Actions::MasterScreen());
2602 insert_action(new Actions::SlaveScreen());
2603 insert_action(new Actions::VolumeUp());
2604 insert_action(new Actions::VolumeDown());
2605 insert_action(new Actions::DeletePlaylistItems());
2606 insert_action(new Actions::DeleteStoredPlaylist());
2607 insert_action(new Actions::DeleteBrowserItems());
2608 insert_action(new Actions::ReplaySong());
2609 insert_action(new Actions::PreviousSong());
2610 insert_action(new Actions::NextSong());
2611 insert_action(new Actions::Pause());
2612 insert_action(new Actions::Stop());
2613 insert_action(new Actions::ExecuteCommand());
2614 insert_action(new Actions::SavePlaylist());
2615 insert_action(new Actions::MoveSortOrderUp());
2616 insert_action(new Actions::MoveSortOrderDown());
2617 insert_action(new Actions::MoveSelectedItemsUp());
2618 insert_action(new Actions::MoveSelectedItemsDown());
2619 insert_action(new Actions::MoveSelectedItemsTo());
2620 insert_action(new Actions::Add());
2621 insert_action(new Actions::SeekForward());
2622 insert_action(new Actions::SeekBackward());
2623 insert_action(new Actions::ToggleDisplayMode());
2624 insert_action(new Actions::ToggleSeparatorsBetweenAlbums());
2625 insert_action(new Actions::ToggleLyricsFetcher());
2626 insert_action(new Actions::ToggleFetchingLyricsInBackground());
2627 insert_action(new Actions::TogglePlayingSongCentering());
2628 insert_action(new Actions::UpdateDatabase());
2629 insert_action(new Actions::JumpToPlayingSong());
2630 insert_action(new Actions::ToggleRepeat());
2631 insert_action(new Actions::Shuffle());
2632 insert_action(new Actions::ToggleRandom());
2633 insert_action(new Actions::StartSearching());
2634 insert_action(new Actions::SaveTagChanges());
2635 insert_action(new Actions::ToggleSingle());
2636 insert_action(new Actions::ToggleConsume());
2637 insert_action(new Actions::ToggleCrossfade());
2638 insert_action(new Actions::SetCrossfade());
2639 insert_action(new Actions::SetVolume());
2640 insert_action(new Actions::EditSong());
2641 insert_action(new Actions::EditLibraryTag());
2642 insert_action(new Actions::EditLibraryAlbum());
2643 insert_action(new Actions::EditDirectoryName());
2644 insert_action(new Actions::EditPlaylistName());
2645 insert_action(new Actions::EditLyrics());
2646 insert_action(new Actions::JumpToBrowser());
2647 insert_action(new Actions::JumpToMediaLibrary());
2648 insert_action(new Actions::JumpToPlaylistEditor());
2649 insert_action(new Actions::ToggleScreenLock());
2650 insert_action(new Actions::JumpToTagEditor());
2651 insert_action(new Actions::JumpToPositionInSong());
2652 insert_action(new Actions::ReverseSelection());
2653 insert_action(new Actions::RemoveSelection());
2654 insert_action(new Actions::SelectAlbum());
2655 insert_action(new Actions::AddSelectedItems());
2656 insert_action(new Actions::CropMainPlaylist());
2657 insert_action(new Actions::CropPlaylist());
2658 insert_action(new Actions::ClearMainPlaylist());
2659 insert_action(new Actions::ClearPlaylist());
2660 insert_action(new Actions::SortPlaylist());
2661 insert_action(new Actions::ReversePlaylist());
2662 insert_action(new Actions::ApplyFilter());
2663 insert_action(new Actions::Find());
2664 insert_action(new Actions::FindItemForward());
2665 insert_action(new Actions::FindItemBackward());
2666 insert_action(new Actions::NextFoundItem());
2667 insert_action(new Actions::PreviousFoundItem());
2668 insert_action(new Actions::ToggleFindMode());
2669 insert_action(new Actions::ToggleReplayGainMode());
2670 insert_action(new Actions::ToggleSpaceMode());
2671 insert_action(new Actions::ToggleAddMode());
2672 insert_action(new Actions::ToggleMouse());
2673 insert_action(new Actions::ToggleBitrateVisibility());
2674 insert_action(new Actions::AddRandomItems());
2675 insert_action(new Actions::ToggleBrowserSortMode());
2676 insert_action(new Actions::ToggleLibraryTagType());
2677 insert_action(new Actions::ToggleMediaLibrarySortMode());
2678 insert_action(new Actions::RefetchLyrics());
2679 insert_action(new Actions::SetSelectedItemsPriority());
2680 insert_action(new Actions::SetVisualizerSampleMultiplier());
2681 insert_action(new Actions::FilterPlaylistOnPriorities());
2682 insert_action(new Actions::ShowSongInfo());
2683 insert_action(new Actions::ShowArtistInfo());
2684 insert_action(new Actions::ShowLyrics());
2685 insert_action(new Actions::Quit());
2686 insert_action(new Actions::NextScreen());
2687 insert_action(new Actions::PreviousScreen());
2688 insert_action(new Actions::ShowHelp());
2689 insert_action(new Actions::ShowPlaylist());
2690 insert_action(new Actions::ShowBrowser());
2691 insert_action(new Actions::ChangeBrowseMode());
2692 insert_action(new Actions::ShowSearchEngine());
2693 insert_action(new Actions::ResetSearchEngine());
2694 insert_action(new Actions::ShowMediaLibrary());
2695 insert_action(new Actions::ToggleMediaLibraryColumnsMode());
2696 insert_action(new Actions::ShowPlaylistEditor());
2697 insert_action(new Actions::ShowTagEditor());
2698 insert_action(new Actions::ShowOutputs());
2699 insert_action(new Actions::ShowVisualizer());
2700 insert_action(new Actions::ShowClock());
2701 insert_action(new Actions::ShowServerInfo());
2706 using Global::wHeader
;
2707 using Global::wFooter
;
2708 using Global::Timer
;
2709 using Global::SeekingInProgress
;
2711 if (!Status::State::totalTime())
2713 Statusbar::print("Unknown item length");
2717 Progressbar::lock();
2720 unsigned songpos
= Status::State::elapsedTime();
2723 int old_timeout
= wFooter
->getTimeout();
2724 wFooter
->setTimeout(500);
2726 auto seekForward
= &Actions::get(Actions::Type::SeekForward
);
2727 auto seekBackward
= &Actions::get(Actions::Type::SeekBackward
);
2729 SeekingInProgress
= true;
2734 unsigned howmuch
= Config
.incremental_seeking
2735 ? (Timer
-t
).total_seconds()/2+Config
.seek_time
2738 Key input
= Key::read(*wFooter
);
2739 auto k
= Bindings
.get(input
);
2740 if (k
.first
== k
.second
|| !k
.first
->isSingle()) // no single action?
2742 auto a
= k
.first
->action();
2743 if (a
== seekForward
)
2745 if (songpos
< Status::State::totalTime())
2746 songpos
= std::min(songpos
+ howmuch
, Status::State::totalTime());
2748 else if (a
== seekBackward
)
2752 if (songpos
< howmuch
)
2761 *wFooter
<< NC::Format::Bold
;
2762 std::string tracklength
;
2763 switch (Config
.design
)
2765 case Design::Classic
:
2767 if (Config
.display_remaining_time
)
2770 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime()-songpos
);
2773 tracklength
+= MPD::Song::ShowTime(songpos
);
2775 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime());
2777 *wFooter
<< NC::XY(wFooter
->getWidth()-tracklength
.length(), 1) << tracklength
;
2779 case Design::Alternative
:
2780 if (Config
.display_remaining_time
)
2783 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime()-songpos
);
2786 tracklength
= MPD::Song::ShowTime(songpos
);
2788 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime());
2789 *wHeader
<< NC::XY(0, 0) << tracklength
<< " ";
2793 *wFooter
<< NC::Format::NoBold
;
2794 Progressbar::draw(songpos
, Status::State::totalTime());
2797 SeekingInProgress
= false;
2798 Mpd
.Seek(Status::State::currentSongPosition(), songpos
);
2800 wFooter
->setTimeout(old_timeout
);
2802 Progressbar::unlock();
2803 Statusbar::unlock();
2806 void findItem(const Find direction
)
2808 using Global::wFooter
;
2810 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
2812 assert(w
->allowsSearching());
2815 Statusbar::put() << "Find " << (direction
== Find::Forward
? "forward" : "backward") << ": ";
2816 std::string findme
= wFooter
->getString();
2817 Statusbar::unlock();
2819 if (!findme
.empty())
2820 Statusbar::print("Searching...");
2822 bool success
= w
->search(findme
);
2828 Statusbar::print("Searching finished");
2830 Statusbar::printf("Unable to find \"%1%\"", findme
);
2832 if (direction
== ::Find::Forward
)
2833 w
->nextFound(Config
.wrapped_search
);
2835 w
->prevFound(Config
.wrapped_search
);
2837 if (myScreen
== myPlaylist
)
2838 myPlaylist
->EnableHighlighting();
2841 void listsChangeFinisher()
2843 if (myScreen
== myLibrary
2844 || myScreen
== myPlaylistEditor
2845 # ifdef HAVE_TAGLIB_H
2846 || myScreen
== myTagEditor
2847 # endif // HAVE_TAGLIB_H
2850 if (myScreen
->activeWindow() == &myLibrary
->Tags
)
2852 myLibrary
->Albums
.clear();
2853 myLibrary
->Albums
.refresh();
2854 myLibrary
->Songs
.clear();
2855 myLibrary
->Songs
.refresh();
2856 myLibrary
->updateTimer();
2858 else if (myScreen
->activeWindow() == &myLibrary
->Albums
)
2860 myLibrary
->Songs
.clear();
2861 myLibrary
->Songs
.refresh();
2862 myLibrary
->updateTimer();
2864 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
))
2866 myPlaylistEditor
->Content
.clear();
2867 myPlaylistEditor
->Content
.refresh();
2868 myPlaylistEditor
->updateTimer();
2870 # ifdef HAVE_TAGLIB_H
2871 else if (myScreen
->activeWindow() == myTagEditor
->Dirs
)
2873 myTagEditor
->Tags
->clear();
2875 # endif // HAVE_TAGLIB_H