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
);
321 // workaround shitty ncurses behavior introduced in >=5.8, when we mysteriously get
322 // a few times after ncmpcpp startup 2^27 code instead of BUTTON{1,3}_RELEASED. since that
323 // 2^27 thing shows constantly instead of BUTTON2_PRESSED, it was redefined to be recognized
324 // as BUTTON2_PRESSED. but clearly we don't want to trigger behavior bound to BUTTON2
325 // after BUTTON{1,3} was pressed. so, here is the workaround: if last event was BUTTON{1,3}_PRESSED,
326 // we MUST get BUTTON{1,3}_RELEASED afterwards. if we get BUTTON2_PRESSED, erroneus behavior
327 // is about to occur and we need to prevent that.
328 if (m_old_mouse_event
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
) && m_mouse_event
.bstate
& BUTTON2_PRESSED
)
330 if (m_mouse_event
.bstate
& BUTTON1_PRESSED
331 && m_mouse_event
.y
== LINES
-(Config
.statusbar_visibility
? 2 : 1)
334 if (Status::State::player() == MPD::psStop
)
336 Mpd
.Seek(Status::State::currentSongPosition(),
337 Status::State::totalTime()*m_mouse_event
.x
/double(COLS
));
339 else if (m_mouse_event
.bstate
& BUTTON1_PRESSED
340 && (Config
.statusbar_visibility
|| Config
.design
== Design::Alternative
)
341 && Status::State::player() != MPD::psStop
342 && m_mouse_event
.y
== (Config
.design
== Design::Alternative
? 1 : LINES
-1)
343 && m_mouse_event
.x
< 9
348 else if ((m_mouse_event
.bstate
& BUTTON2_PRESSED
|| m_mouse_event
.bstate
& BUTTON4_PRESSED
)
349 && (Config
.header_visibility
|| Config
.design
== Design::Alternative
)
350 && m_mouse_event
.y
== 0 && size_t(m_mouse_event
.x
) > COLS
-VolumeState
.length()
353 if (m_mouse_event
.bstate
& BUTTON2_PRESSED
)
354 get(Type::VolumeDown
).execute();
356 get(Type::VolumeUp
).execute();
358 else if (m_mouse_event
.bstate
& (BUTTON1_PRESSED
| BUTTON2_PRESSED
| BUTTON3_PRESSED
| BUTTON4_PRESSED
))
359 myScreen
->mouseButtonPressed(m_mouse_event
);
364 myScreen
->scroll(NC::Scroll::Up
);
365 listsChangeFinisher();
368 void ScrollDown::run()
370 myScreen
->scroll(NC::Scroll::Down
);
371 listsChangeFinisher();
374 bool ScrollUpArtist::canBeRun() const
376 return proxySongList(myScreen
);
379 void ScrollUpArtist::run()
381 auto pl
= proxySongList(myScreen
);
385 size_t pos
= pl
.choice();
386 if (MPD::Song
*s
= pl
.getSong(pos
))
388 std::string artist
= s
->getArtist();
391 s
= pl
.getSong(--pos
);
392 if (!s
|| s
->getArtist() != artist
)
399 bool ScrollUpAlbum::canBeRun() const
401 return proxySongList(myScreen
);
404 void ScrollUpAlbum::run()
406 auto pl
= proxySongList(myScreen
);
410 size_t pos
= pl
.choice();
411 if (MPD::Song
*s
= pl
.getSong(pos
))
413 std::string album
= s
->getAlbum();
416 s
= pl
.getSong(--pos
);
417 if (!s
|| s
->getAlbum() != album
)
424 bool ScrollDownArtist::canBeRun() const
426 return proxySongList(myScreen
);
429 void ScrollDownArtist::run()
431 auto pl
= proxySongList(myScreen
);
435 size_t pos
= pl
.choice();
436 if (MPD::Song
*s
= pl
.getSong(pos
))
438 std::string artist
= s
->getArtist();
439 while (pos
< pl
.size() - 1)
441 s
= pl
.getSong(++pos
);
442 if (!s
|| s
->getArtist() != artist
)
449 bool ScrollDownAlbum::canBeRun() const
451 return proxySongList(myScreen
);
454 void ScrollDownAlbum::run()
456 auto pl
= proxySongList(myScreen
);
460 size_t pos
= pl
.choice();
461 if (MPD::Song
*s
= pl
.getSong(pos
))
463 std::string album
= s
->getAlbum();
464 while (pos
< pl
.size() - 1)
466 s
= pl
.getSong(++pos
);
467 if (!s
|| s
->getAlbum() != album
)
476 myScreen
->scroll(NC::Scroll::PageUp
);
477 listsChangeFinisher();
482 myScreen
->scroll(NC::Scroll::PageDown
);
483 listsChangeFinisher();
488 myScreen
->scroll(NC::Scroll::Home
);
489 listsChangeFinisher();
494 myScreen
->scroll(NC::Scroll::End
);
495 listsChangeFinisher();
498 void ToggleInterface::run()
500 switch (Config
.design
)
502 case Design::Classic
:
503 Config
.design
= Design::Alternative
;
504 Config
.statusbar_visibility
= false;
506 case Design::Alternative
:
507 Config
.design
= Design::Classic
;
508 Config
.statusbar_visibility
= OriginalStatusbarVisibility
;
511 setWindowsDimensions();
512 Progressbar::unlock();
515 Status::Changes::mixer();
516 Status::Changes::elapsedTime(false);
517 Statusbar::printf("User interface: %1%", Config
.design
);
520 bool JumpToParentDirectory::canBeRun() const
522 return (myScreen
== myBrowser
)
523 # ifdef HAVE_TAGLIB_H
524 || (myScreen
->activeWindow() == myTagEditor
->Dirs
)
525 # endif // HAVE_TAGLIB_H
529 void JumpToParentDirectory::run()
531 if (myScreen
== myBrowser
)
533 if (myBrowser
->CurrentDir() != "/")
535 myBrowser
->main().reset();
536 myBrowser
->enterPressed();
539 # ifdef HAVE_TAGLIB_H
540 else if (myScreen
== myTagEditor
)
542 if (myTagEditor
->CurrentDir() != "/")
544 myTagEditor
->Dirs
->reset();
545 myTagEditor
->enterPressed();
548 # endif // HAVE_TAGLIB_H
551 void PressEnter::run()
553 myScreen
->enterPressed();
556 void PressSpace::run()
558 myScreen
->spacePressed();
561 bool PreviousColumn::canBeRun() const
563 auto hc
= hasColumns(myScreen
);
564 return hc
&& hc
->previousColumnAvailable();
567 void PreviousColumn::run()
569 hasColumns(myScreen
)->previousColumn();
572 bool NextColumn::canBeRun() const
574 auto hc
= hasColumns(myScreen
);
575 return hc
&& hc
->nextColumnAvailable();
578 void NextColumn::run()
580 hasColumns(myScreen
)->nextColumn();
583 bool MasterScreen::canBeRun() const
585 using Global::myLockedScreen
;
586 using Global::myInactiveScreen
;
588 return myLockedScreen
590 && myLockedScreen
!= myScreen
591 && myScreen
->isMergable();
594 void MasterScreen::run()
596 using Global::myInactiveScreen
;
597 using Global::myLockedScreen
;
599 myInactiveScreen
= myScreen
;
600 myScreen
= myLockedScreen
;
604 bool SlaveScreen::canBeRun() const
606 using Global::myLockedScreen
;
607 using Global::myInactiveScreen
;
609 return myLockedScreen
611 && myLockedScreen
== myScreen
612 && myScreen
->isMergable();
615 void SlaveScreen::run()
617 using Global::myInactiveScreen
;
618 using Global::myLockedScreen
;
620 myScreen
= myInactiveScreen
;
621 myInactiveScreen
= myLockedScreen
;
627 int volume
= std::min(Status::State::volume()+Config
.volume_change_step
, 100u);
628 Mpd
.SetVolume(volume
);
631 void VolumeDown::run()
633 int volume
= std::max(int(Status::State::volume()-Config
.volume_change_step
), 0);
634 Mpd
.SetVolume(volume
);
637 bool DeletePlaylistItems::canBeRun() const
639 return (myScreen
== myPlaylist
&& !myPlaylist
->main().empty())
640 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
) && !myPlaylistEditor
->Content
.empty());
643 void DeletePlaylistItems::run()
645 if (myScreen
== myPlaylist
)
647 Statusbar::print("Deleting items...");
648 auto delete_fun
= boost::bind(&MPD::Connection::Delete
, _1
, _2
);
649 deleteSelectedSongs(myPlaylist
->main(), delete_fun
);
650 Statusbar::print("Item(s) deleted");
652 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Content
))
654 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
655 auto delete_fun
= boost::bind(&MPD::Connection::PlaylistDelete
, _1
, playlist
, _2
);
656 Statusbar::print("Deleting items...");
657 deleteSelectedSongs(myPlaylistEditor
->Content
, delete_fun
);
658 Statusbar::print("Item(s) deleted");
662 bool DeleteBrowserItems::canBeRun() const
664 auto check_if_deletion_allowed
= []() {
665 if (Config
.allow_for_physical_item_deletion
)
669 Statusbar::print("Flag \"allow_for_physical_item_deletion\" needs to be enabled in configuration file");
673 return myScreen
== myBrowser
674 && !myBrowser
->main().empty()
675 && isMPDMusicDirSet()
676 && check_if_deletion_allowed();
679 void DeleteBrowserItems::run()
681 boost::format question
;
682 if (hasSelected(myBrowser
->main().begin(), myBrowser
->main().end()))
683 question
= boost::format("Delete selected items?");
686 MPD::Item
&item
= myBrowser
->main().current().value();
687 std::string iname
= item
.type
== MPD::itSong
? item
.song
->getName() : item
.name
;
688 question
= boost::format("Delete %1% \"%2%\"?")
689 % itemTypeToString(item
.type
) % wideShorten(iname
, COLS
-question
.size()-10);
691 bool yes
= askYesNoQuestion(question
, Status::trace
);
695 auto list
= getSelectedOrCurrent(myBrowser
->main().begin(), myBrowser
->main().end(), myBrowser
->main().currentI());
696 for (auto it
= list
.begin(); it
!= list
.end(); ++it
)
698 const MPD::Item
&i
= (*it
)->value();
699 std::string iname
= i
.type
== MPD::itSong
? i
.song
->getName() : i
.name
;
701 if (myBrowser
->deleteItem(i
, errmsg
))
703 const char msg
[] = "\"%1%\" deleted";
704 Statusbar::printf(msg
, wideShorten(iname
, COLS
-const_strlen(msg
)));
708 Statusbar::print(errmsg
);
715 if (myBrowser
->isLocal())
716 myBrowser
->GetDirectory(myBrowser
->CurrentDir());
718 Mpd
.UpdateDirectory(myBrowser
->CurrentDir());
722 Statusbar::print("Aborted");
725 bool DeleteStoredPlaylist::canBeRun() const
727 return myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
);
730 void DeleteStoredPlaylist::run()
732 if (myPlaylistEditor
->Playlists
.empty())
734 boost::format question
;
735 if (hasSelected(myPlaylistEditor
->Playlists
.begin(), myPlaylistEditor
->Playlists
.end()))
736 question
= boost::format("Delete selected playlists?");
738 question
= boost::format("Delete playlist \"%1%\"?")
739 % wideShorten(myPlaylistEditor
->Playlists
.current().value(), COLS
-question
.size()-10);
740 bool yes
= askYesNoQuestion(question
, Status::trace
);
743 auto list
= getSelectedOrCurrent(myPlaylistEditor
->Playlists
.begin(), myPlaylistEditor
->Playlists
.end(), myPlaylistEditor
->Playlists
.currentI());
744 for (auto it
= list
.begin(); it
!= list
.end(); ++it
)
745 Mpd
.DeletePlaylist((*it
)->value());
746 Statusbar::printf("%1% deleted", list
.size() == 1 ? "Playlist" : "Playlists");
749 Statusbar::print("Aborted");
752 void ReplaySong::run()
754 if (Status::State::player() != MPD::psStop
)
755 Mpd
.Seek(Status::State::currentSongPosition(), 0);
758 void PreviousSong::run()
773 void SavePlaylist::run()
775 using Global::wFooter
;
778 Statusbar::put() << "Save playlist as: ";
779 std::string playlist_name
= wFooter
->getString();
781 if (playlist_name
.find("/") != std::string::npos
)
783 Statusbar::print("Playlist name must not contain slashes");
786 if (!playlist_name
.empty())
788 if (myPlaylist
->main().isFiltered())
790 Mpd
.StartCommandsList();
791 for (size_t i
= 0; i
< myPlaylist
->main().size(); ++i
)
792 Mpd
.AddToPlaylist(playlist_name
, myPlaylist
->main()[i
].value());
793 Mpd
.CommitCommandsList();
794 Statusbar::printf("Filtered items added to playlist \"%1%\"", playlist_name
);
800 Mpd
.SavePlaylist(playlist_name
);
801 Statusbar::printf("Playlist saved as \"%1%\"", playlist_name
);
803 catch (MPD::ServerError
&e
)
805 if (e
.code() == MPD_SERVER_ERROR_EXIST
)
807 bool yes
= askYesNoQuestion(
808 boost::format("Playlist \"%1%\" already exists, overwrite?") % playlist_name
,
813 Mpd
.DeletePlaylist(playlist_name
);
814 Mpd
.SavePlaylist(playlist_name
);
815 Statusbar::print("Playlist overwritten");
818 Statusbar::print("Aborted");
819 if (myScreen
== myPlaylist
)
820 myPlaylist
->EnableHighlighting();
827 if (!myBrowser
->isLocal()
828 && myBrowser
->CurrentDir() == "/"
829 && !myBrowser
->main().empty())
830 myBrowser
->GetDirectory(myBrowser
->CurrentDir());
838 void ExecuteCommand::run()
840 using Global::wFooter
;
842 Statusbar::put() << NC::Format::Bold
<< ":" << NC::Format::NoBold
;
843 wFooter
->setGetStringHelper(Statusbar::Helpers::TryExecuteImmediateCommand());
844 std::string cmd_name
= wFooter
->getString();
845 wFooter
->setGetStringHelper(Statusbar::Helpers::getString
);
847 if (cmd_name
.empty())
849 auto cmd
= Bindings
.findCommand(cmd_name
);
852 Statusbar::printf(1, "Executing %1%...", cmd_name
);
853 bool res
= cmd
->binding().execute();
854 Statusbar::printf("Execution of command \"%1%\" %2%.",
855 cmd_name
, res
? "successful" : "unsuccessful"
859 Statusbar::printf("No command named \"%1%\"", cmd_name
);
862 bool MoveSortOrderUp::canBeRun() const
864 return myScreen
== mySortPlaylistDialog
;
867 void MoveSortOrderUp::run()
869 mySortPlaylistDialog
->moveSortOrderUp();
872 bool MoveSortOrderDown::canBeRun() const
874 return myScreen
== mySortPlaylistDialog
;
877 void MoveSortOrderDown::run()
879 mySortPlaylistDialog
->moveSortOrderDown();
882 bool MoveSelectedItemsUp::canBeRun() const
884 return ((myScreen
== myPlaylist
885 && !myPlaylist
->main().empty()
886 && !myPlaylist
->isFiltered())
887 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
)
888 && !myPlaylistEditor
->Content
.empty()
889 && !myPlaylistEditor
->isContentFiltered()));
892 void MoveSelectedItemsUp::run()
894 if (myScreen
== myPlaylist
)
896 moveSelectedItemsUp(myPlaylist
->main(), boost::bind(&MPD::Connection::Move
, _1
, _2
, _3
));
898 else if (myScreen
== myPlaylistEditor
)
900 assert(!myPlaylistEditor
->Playlists
.empty());
901 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
902 auto move_fun
= boost::bind(&MPD::Connection::PlaylistMove
, _1
, playlist
, _2
, _3
);
903 moveSelectedItemsUp(myPlaylistEditor
->Content
, move_fun
);
907 bool MoveSelectedItemsDown::canBeRun() const
909 return ((myScreen
== myPlaylist
910 && !myPlaylist
->main().empty()
911 && !myPlaylist
->isFiltered())
912 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
)
913 && !myPlaylistEditor
->Content
.empty()
914 && !myPlaylistEditor
->isContentFiltered()));
917 void MoveSelectedItemsDown::run()
919 if (myScreen
== myPlaylist
)
921 moveSelectedItemsDown(myPlaylist
->main(), boost::bind(&MPD::Connection::Move
, _1
, _2
, _3
));
923 else if (myScreen
== myPlaylistEditor
)
925 assert(!myPlaylistEditor
->Playlists
.empty());
926 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
927 auto move_fun
= boost::bind(&MPD::Connection::PlaylistMove
, _1
, playlist
, _2
, _3
);
928 moveSelectedItemsDown(myPlaylistEditor
->Content
, move_fun
);
932 bool MoveSelectedItemsTo::canBeRun() const
934 return myScreen
== myPlaylist
935 || myScreen
->isActiveWindow(myPlaylistEditor
->Content
);
938 void MoveSelectedItemsTo::run()
940 if (myScreen
== myPlaylist
)
942 if (!myPlaylist
->main().empty())
943 moveSelectedItemsTo(myPlaylist
->main(), boost::bind(&MPD::Connection::Move
, _1
, _2
, _3
));
947 assert(!myPlaylistEditor
->Playlists
.empty());
948 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
949 auto move_fun
= boost::bind(&MPD::Connection::PlaylistMove
, _1
, playlist
, _2
, _3
);
950 moveSelectedItemsTo(myPlaylistEditor
->Content
, move_fun
);
954 bool Add::canBeRun() const
956 return myScreen
!= myPlaylistEditor
957 || !myPlaylistEditor
->Playlists
.empty();
962 using Global::wFooter
;
965 Statusbar::put() << (myScreen
== myPlaylistEditor
? "Add to playlist: " : "Add: ");
966 std::string path
= wFooter
->getString();
970 Statusbar::put() << "Adding...";
972 if (myScreen
== myPlaylistEditor
)
973 Mpd
.AddToPlaylist(myPlaylistEditor
->Playlists
.current().value(), path
);
976 const char lastfm_url
[] = "lastfm://";
977 if (path
.compare(0, const_strlen(lastfm_url
), lastfm_url
) == 0
978 || path
.find(".asx", path
.length()-4) != std::string::npos
979 || path
.find(".cue", path
.length()-4) != std::string::npos
980 || path
.find(".m3u", path
.length()-4) != std::string::npos
981 || path
.find(".pls", path
.length()-4) != std::string::npos
982 || path
.find(".xspf", path
.length()-5) != std::string::npos
)
983 Mpd
.LoadPlaylist(path
);
990 bool SeekForward::canBeRun() const
992 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
995 void SeekForward::run()
1000 bool SeekBackward::canBeRun() const
1002 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
1005 void SeekBackward::run()
1010 bool ToggleDisplayMode::canBeRun() const
1012 return myScreen
== myPlaylist
1013 || myScreen
== myBrowser
1014 || myScreen
== mySearcher
1015 || myScreen
->isActiveWindow(myPlaylistEditor
->Content
);
1018 void ToggleDisplayMode::run()
1020 if (myScreen
== myPlaylist
)
1022 switch (Config
.playlist_display_mode
)
1024 case DisplayMode::Classic
:
1025 Config
.playlist_display_mode
= DisplayMode::Columns
;
1026 myPlaylist
->main().setItemDisplayer(boost::bind(
1027 Display::SongsInColumns
, _1
, myPlaylist
->proxySongList()
1029 if (Config
.titles_visibility
)
1030 myPlaylist
->main().setTitle(Display::Columns(myPlaylist
->main().getWidth()));
1032 myPlaylist
->main().setTitle("");
1034 case DisplayMode::Columns
:
1035 Config
.playlist_display_mode
= DisplayMode::Classic
;
1036 myPlaylist
->main().setItemDisplayer(boost::bind(
1037 Display::Songs
, _1
, myPlaylist
->proxySongList(), Config
.song_list_format
1039 myPlaylist
->main().setTitle("");
1041 Statusbar::printf("Playlist display mode: %1%", Config
.playlist_display_mode
);
1043 else if (myScreen
== myBrowser
)
1045 switch (Config
.browser_display_mode
)
1047 case DisplayMode::Classic
:
1048 Config
.browser_display_mode
= DisplayMode::Columns
;
1049 if (Config
.titles_visibility
)
1050 myBrowser
->main().setTitle(Display::Columns(myBrowser
->main().getWidth()));
1052 myBrowser
->main().setTitle("");
1054 case DisplayMode::Columns
:
1055 Config
.browser_display_mode
= DisplayMode::Classic
;
1056 myBrowser
->main().setTitle("");
1059 Statusbar::printf("Browser display mode: %1%", Config
.browser_display_mode
);
1061 else if (myScreen
== mySearcher
)
1063 switch (Config
.search_engine_display_mode
)
1065 case DisplayMode::Classic
:
1066 Config
.search_engine_display_mode
= DisplayMode::Columns
;
1068 case DisplayMode::Columns
:
1069 Config
.search_engine_display_mode
= DisplayMode::Classic
;
1072 Statusbar::printf("Search engine display mode: %1%", Config
.search_engine_display_mode
);
1073 if (mySearcher
->main().size() > SearchEngine::StaticOptions
)
1074 mySearcher
->main().setTitle(
1075 Config
.search_engine_display_mode
== DisplayMode::Columns
1076 && Config
.titles_visibility
1077 ? Display::Columns(mySearcher
->main().getWidth())
1081 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Content
))
1083 switch (Config
.playlist_editor_display_mode
)
1085 case DisplayMode::Classic
:
1086 Config
.playlist_editor_display_mode
= DisplayMode::Columns
;
1087 myPlaylistEditor
->Content
.setItemDisplayer(boost::bind(
1088 Display::SongsInColumns
, _1
, myPlaylistEditor
->contentProxyList()
1091 case DisplayMode::Columns
:
1092 Config
.playlist_editor_display_mode
= DisplayMode::Classic
;
1093 myPlaylistEditor
->Content
.setItemDisplayer(boost::bind(
1094 Display::Songs
, _1
, myPlaylistEditor
->contentProxyList(), Config
.song_list_format
1098 Statusbar::printf("Playlist editor display mode: %1%", Config
.playlist_editor_display_mode
);
1102 bool ToggleSeparatorsBetweenAlbums::canBeRun() const
1107 void ToggleSeparatorsBetweenAlbums::run()
1109 Config
.playlist_separate_albums
= !Config
.playlist_separate_albums
;
1110 Statusbar::printf("Separators between albums: %1%",
1111 Config
.playlist_separate_albums
? "on" : "off"
1115 #ifndef HAVE_CURL_CURL_H
1116 bool ToggleLyricsFetcher::canBeRun() const
1120 #endif // NOT HAVE_CURL_CURL_H
1122 void ToggleLyricsFetcher::run()
1124 # ifdef HAVE_CURL_CURL_H
1125 myLyrics
->ToggleFetcher();
1126 # endif // HAVE_CURL_CURL_H
1129 #ifndef HAVE_CURL_CURL_H
1130 bool ToggleFetchingLyricsInBackground::canBeRun() const
1134 #endif // NOT HAVE_CURL_CURL_H
1136 void ToggleFetchingLyricsInBackground::run()
1138 # ifdef HAVE_CURL_CURL_H
1139 Config
.fetch_lyrics_in_background
= !Config
.fetch_lyrics_in_background
;
1140 Statusbar::printf("Fetching lyrics for playing songs in background: %1%",
1141 Config
.fetch_lyrics_in_background
? "on" : "off"
1143 # endif // HAVE_CURL_CURL_H
1146 void TogglePlayingSongCentering::run()
1148 Config
.autocenter_mode
= !Config
.autocenter_mode
;
1149 Statusbar::printf("Centering playing song: %1%",
1150 Config
.autocenter_mode
? "on" : "off"
1152 if (Config
.autocenter_mode
&& !myPlaylist
->main().isFiltered())
1154 auto s
= myPlaylist
->nowPlayingSong();
1156 myPlaylist
->main().highlight(s
.getPosition());
1160 void UpdateDatabase::run()
1162 if (myScreen
== myBrowser
)
1163 Mpd
.UpdateDirectory(myBrowser
->CurrentDir());
1164 # ifdef HAVE_TAGLIB_H
1165 else if (myScreen
== myTagEditor
)
1166 Mpd
.UpdateDirectory(myTagEditor
->CurrentDir());
1167 # endif // HAVE_TAGLIB_H
1169 Mpd
.UpdateDirectory("/");
1172 bool JumpToPlayingSong::canBeRun() const
1174 return ((myScreen
== myPlaylist
&& !myPlaylist
->isFiltered())
1175 || myScreen
== myBrowser
1176 || myScreen
== myLibrary
)
1177 && !myPlaylist
->nowPlayingSong().empty();
1180 void JumpToPlayingSong::run()
1182 auto s
= myPlaylist
->nowPlayingSong();
1183 if (myScreen
== myPlaylist
)
1185 myPlaylist
->main().highlight(s
.getPosition());
1187 else if (myScreen
== myBrowser
)
1189 myBrowser
->LocateSong(s
);
1192 else if (myScreen
== myLibrary
)
1194 myLibrary
->LocateSong(s
);
1198 void ToggleRepeat::run()
1200 Mpd
.SetRepeat(!Status::State::repeat());
1208 void ToggleRandom::run()
1210 Mpd
.SetRandom(!Status::State::random());
1213 bool StartSearching::canBeRun() const
1215 return myScreen
== mySearcher
&& !mySearcher
->main()[0].isInactive();
1218 void StartSearching::run()
1220 mySearcher
->main().highlight(SearchEngine::SearchButton
);
1221 mySearcher
->main().setHighlighting(0);
1222 mySearcher
->main().refresh();
1223 mySearcher
->main().setHighlighting(1);
1224 mySearcher
->enterPressed();
1227 bool SaveTagChanges::canBeRun() const
1229 # ifdef HAVE_TAGLIB_H
1230 return myScreen
== myTinyTagEditor
1231 || myScreen
->activeWindow() == myTagEditor
->TagTypes
;
1234 # endif // HAVE_TAGLIB_H
1237 void SaveTagChanges::run()
1239 # ifdef HAVE_TAGLIB_H
1240 if (myScreen
== myTinyTagEditor
)
1242 myTinyTagEditor
->main().highlight(myTinyTagEditor
->main().size()-2); // Save
1243 myTinyTagEditor
->enterPressed();
1245 else if (myScreen
->activeWindow() == myTagEditor
->TagTypes
)
1247 myTagEditor
->TagTypes
->highlight(myTagEditor
->TagTypes
->size()-1); // Save
1248 myTagEditor
->enterPressed();
1250 # endif // HAVE_TAGLIB_H
1253 void ToggleSingle::run()
1255 Mpd
.SetSingle(!Status::State::single());
1258 void ToggleConsume::run()
1260 Mpd
.SetConsume(!Status::State::consume());
1263 void ToggleCrossfade::run()
1265 Mpd
.SetCrossfade(Status::State::crossfade() ? 0 : Config
.crossfade_time
);
1268 void SetCrossfade::run()
1270 using Global::wFooter
;
1273 Statusbar::put() << "Set crossfade to: ";
1274 std::string crossfade
= wFooter
->getString();
1275 Statusbar::unlock();
1276 int cf
= fromString
<unsigned>(crossfade
);
1277 lowerBoundCheck(cf
, 1);
1278 Config
.crossfade_time
= cf
;
1279 Mpd
.SetCrossfade(cf
);
1282 void SetVolume::run()
1284 using Global::wFooter
;
1287 Statusbar::put() << "Set volume to: ";
1288 std::string strvolume
= wFooter
->getString();
1289 Statusbar::unlock();
1290 int volume
= fromString
<unsigned>(strvolume
);
1291 boundsCheck(volume
, 0, 100);
1292 Mpd
.SetVolume(volume
);
1293 Statusbar::printf("Volume set to %1%%%", volume
);
1296 bool EditSong::canBeRun() const
1298 # ifdef HAVE_TAGLIB_H
1299 return currentSong(myScreen
)
1300 && isMPDMusicDirSet();
1303 # endif // HAVE_TAGLIB_H
1306 void EditSong::run()
1308 # ifdef HAVE_TAGLIB_H
1309 auto s
= currentSong(myScreen
);
1310 myTinyTagEditor
->SetEdited(*s
);
1311 myTinyTagEditor
->switchTo();
1312 # endif // HAVE_TAGLIB_H
1315 bool EditLibraryTag::canBeRun() const
1317 # ifdef HAVE_TAGLIB_H
1318 return myScreen
->isActiveWindow(myLibrary
->Tags
)
1319 && !myLibrary
->Tags
.empty()
1320 && isMPDMusicDirSet();
1323 # endif // HAVE_TAGLIB_H
1326 void EditLibraryTag::run()
1328 # ifdef HAVE_TAGLIB_H
1329 using Global::wFooter
;
1332 Statusbar::put() << NC::Format::Bold
<< tagTypeToString(Config
.media_lib_primary_tag
) << NC::Format::NoBold
<< ": ";
1333 std::string new_tag
= wFooter
->getString(myLibrary
->Tags
.current().value().tag());
1334 Statusbar::unlock();
1335 if (!new_tag
.empty() && new_tag
!= myLibrary
->Tags
.current().value().tag())
1337 Statusbar::print("Updating tags...");
1339 Mpd
.AddSearch(Config
.media_lib_primary_tag
, myLibrary
->Tags
.current().value().tag());
1340 MPD::MutableSong::SetFunction set
= tagTypeToSetFunction(Config
.media_lib_primary_tag
);
1342 bool success
= true;
1343 std::string dir_to_update
;
1344 Mpd
.CommitSearchSongs([set
, &dir_to_update
, &new_tag
, &success
](MPD::Song s
) {
1347 MPD::MutableSong ms
= s
;
1348 ms
.setTags(set
, new_tag
, Config
.tags_separator
);
1349 Statusbar::printf("Updating tags in \"%1%\"...", ms
.getName());
1350 std::string path
= Config
.mpd_music_dir
+ ms
.getURI();
1351 if (!Tags::write(ms
))
1353 const char msg
[] = "Error while updating tags in \"%1%\"";
1354 Statusbar::printf(msg
, wideShorten(ms
.getURI(), COLS
-const_strlen(msg
)));
1357 if (dir_to_update
.empty())
1358 dir_to_update
= s
.getURI();
1360 dir_to_update
= getSharedDirectory(dir_to_update
, s
.getURI());
1364 Mpd
.UpdateDirectory(dir_to_update
);
1365 Statusbar::print("Tags updated successfully");
1368 # endif // HAVE_TAGLIB_H
1371 bool EditLibraryAlbum::canBeRun() const
1373 # ifdef HAVE_TAGLIB_H
1374 return myScreen
->isActiveWindow(myLibrary
->Albums
)
1375 && !myLibrary
->Albums
.empty()
1376 && isMPDMusicDirSet();
1379 # endif // HAVE_TAGLIB_H
1382 void EditLibraryAlbum::run()
1384 # ifdef HAVE_TAGLIB_H
1385 using Global::wFooter
;
1388 Statusbar::put() << NC::Format::Bold
<< "Album: " << NC::Format::NoBold
;
1389 std::string new_album
= wFooter
->getString(myLibrary
->Albums
.current().value().entry().album());
1390 Statusbar::unlock();
1391 if (!new_album
.empty() && new_album
!= myLibrary
->Albums
.current().value().entry().album())
1394 Statusbar::print("Updating tags...");
1395 for (size_t i
= 0; i
< myLibrary
->Songs
.size(); ++i
)
1397 Statusbar::printf("Updating tags in \"%1%\"...", myLibrary
->Songs
[i
].value().getName());
1398 std::string path
= Config
.mpd_music_dir
+ myLibrary
->Songs
[i
].value().getURI();
1399 TagLib::FileRef
f(path
.c_str());
1402 const char msg
[] = "Error while opening file \"%1%\"";
1403 Statusbar::printf(msg
, wideShorten(myLibrary
->Songs
[i
].value().getURI(), COLS
-const_strlen(msg
)));
1407 f
.tag()->setAlbum(ToWString(new_album
));
1410 const char msg
[] = "Error while writing tags in \"%1%\"";
1411 Statusbar::printf(msg
, wideShorten(myLibrary
->Songs
[i
].value().getURI(), COLS
-const_strlen(msg
)));
1418 Mpd
.UpdateDirectory(getSharedDirectory(myLibrary
->Songs
.beginV(), myLibrary
->Songs
.endV()));
1419 Statusbar::print("Tags updated successfully");
1422 # endif // HAVE_TAGLIB_H
1425 bool EditDirectoryName::canBeRun() const
1427 return ((myScreen
== myBrowser
1428 && !myBrowser
->main().empty()
1429 && myBrowser
->main().current().value().type
== MPD::itDirectory
)
1430 # ifdef HAVE_TAGLIB_H
1431 || (myScreen
->activeWindow() == myTagEditor
->Dirs
1432 && !myTagEditor
->Dirs
->empty()
1433 && myTagEditor
->Dirs
->choice() > 0)
1434 # endif // HAVE_TAGLIB_H
1435 ) && isMPDMusicDirSet();
1438 void EditDirectoryName::run()
1440 using Global::wFooter
;
1442 if (myScreen
== myBrowser
)
1444 std::string old_dir
= myBrowser
->main().current().value().name
;
1446 Statusbar::put() << NC::Format::Bold
<< "Directory: " << NC::Format::NoBold
;
1447 std::string new_dir
= wFooter
->getString(old_dir
);
1448 Statusbar::unlock();
1449 if (!new_dir
.empty() && new_dir
!= old_dir
)
1451 std::string full_old_dir
;
1452 if (!myBrowser
->isLocal())
1453 full_old_dir
+= Config
.mpd_music_dir
;
1454 full_old_dir
+= old_dir
;
1455 std::string full_new_dir
;
1456 if (!myBrowser
->isLocal())
1457 full_new_dir
+= Config
.mpd_music_dir
;
1458 full_new_dir
+= new_dir
;
1459 int rename_result
= rename(full_old_dir
.c_str(), full_new_dir
.c_str());
1460 if (rename_result
== 0)
1462 const char msg
[] = "Directory renamed to \"%1%\"";
1463 Statusbar::printf(msg
, wideShorten(new_dir
, COLS
-const_strlen(msg
)));
1464 if (!myBrowser
->isLocal())
1465 Mpd
.UpdateDirectory(getSharedDirectory(old_dir
, new_dir
));
1466 myBrowser
->GetDirectory(myBrowser
->CurrentDir());
1470 const char msg
[] = "Couldn't rename \"%1%\": %s";
1471 Statusbar::printf(msg
, wideShorten(old_dir
, COLS
-const_strlen(msg
)-25), strerror(errno
));
1475 # ifdef HAVE_TAGLIB_H
1476 else if (myScreen
->activeWindow() == myTagEditor
->Dirs
)
1478 std::string old_dir
= myTagEditor
->Dirs
->current().value().first
;
1480 Statusbar::put() << NC::Format::Bold
<< "Directory: " << NC::Format::NoBold
;
1481 std::string new_dir
= wFooter
->getString(old_dir
);
1482 Statusbar::unlock();
1483 if (!new_dir
.empty() && new_dir
!= old_dir
)
1485 std::string full_old_dir
= Config
.mpd_music_dir
+ myTagEditor
->CurrentDir() + "/" + old_dir
;
1486 std::string full_new_dir
= Config
.mpd_music_dir
+ myTagEditor
->CurrentDir() + "/" + new_dir
;
1487 if (rename(full_old_dir
.c_str(), full_new_dir
.c_str()) == 0)
1489 const char msg
[] = "Directory renamed to \"%1%\"";
1490 Statusbar::printf(msg
, wideShorten(new_dir
, COLS
-const_strlen(msg
)));
1491 Mpd
.UpdateDirectory(myTagEditor
->CurrentDir());
1495 const char msg
[] = "Couldn't rename \"%1%\": %2%";
1496 Statusbar::printf(msg
, wideShorten(old_dir
, COLS
-const_strlen(msg
)-25), strerror(errno
));
1500 # endif // HAVE_TAGLIB_H
1503 bool EditPlaylistName::canBeRun() const
1505 return (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
)
1506 && !myPlaylistEditor
->Playlists
.empty())
1507 || (myScreen
== myBrowser
1508 && !myBrowser
->main().empty()
1509 && myBrowser
->main().current().value().type
== MPD::itPlaylist
);
1512 void EditPlaylistName::run()
1514 using Global::wFooter
;
1516 std::string old_name
;
1517 if (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
))
1518 old_name
= myPlaylistEditor
->Playlists
.current().value();
1520 old_name
= myBrowser
->main().current().value().name
;
1522 Statusbar::put() << NC::Format::Bold
<< "Playlist: " << NC::Format::NoBold
;
1523 std::string new_name
= wFooter
->getString(old_name
);
1524 Statusbar::unlock();
1525 if (!new_name
.empty() && new_name
!= old_name
)
1527 Mpd
.Rename(old_name
, new_name
);
1528 const char msg
[] = "Playlist renamed to \"%1%\"";
1529 Statusbar::printf(msg
, wideShorten(new_name
, COLS
-const_strlen(msg
)));
1530 if (!myBrowser
->isLocal())
1531 myBrowser
->GetDirectory("/");
1535 bool EditLyrics::canBeRun() const
1537 return myScreen
== myLyrics
;
1540 void EditLyrics::run()
1545 bool JumpToBrowser::canBeRun() const
1547 return currentSong(myScreen
);
1550 void JumpToBrowser::run()
1552 auto s
= currentSong(myScreen
);
1553 myBrowser
->LocateSong(*s
);
1556 bool JumpToMediaLibrary::canBeRun() const
1558 return currentSong(myScreen
);
1561 void JumpToMediaLibrary::run()
1563 auto s
= currentSong(myScreen
);
1564 myLibrary
->LocateSong(*s
);
1567 bool JumpToPlaylistEditor::canBeRun() const
1569 return myScreen
== myBrowser
1570 && myBrowser
->main().current().value().type
== MPD::itPlaylist
;
1573 void JumpToPlaylistEditor::run()
1575 myPlaylistEditor
->Locate(myBrowser
->main().current().value().name
);
1578 void ToggleScreenLock::run()
1580 using Global::wFooter
;
1581 using Global::myLockedScreen
;
1583 if (myLockedScreen
!= 0)
1585 BaseScreen::unlock();
1586 Actions::setResizeFlags();
1588 Statusbar::print("Screen unlocked");
1592 int part
= Config
.locked_screen_width_part
*100;
1593 if (Config
.ask_for_locked_screen_width_part
)
1596 Statusbar::put() << "% of the locked screen's width to be reserved (20-80): ";
1597 std::string strpart
= wFooter
->getString(boost::lexical_cast
<std::string
>(part
));
1598 Statusbar::unlock();
1599 part
= fromString
<unsigned>(strpart
);
1601 boundsCheck(part
, 20, 80);
1602 Config
.locked_screen_width_part
= part
/100.0;
1603 if (myScreen
->lock())
1604 Statusbar::printf("Screen locked (with %1%%% width)", part
);
1606 Statusbar::print("Current screen can't be locked");
1610 bool JumpToTagEditor::canBeRun() const
1612 # ifdef HAVE_TAGLIB_H
1613 return currentSong(myScreen
)
1614 && isMPDMusicDirSet();
1617 # endif // HAVE_TAGLIB_H
1620 void JumpToTagEditor::run()
1622 # ifdef HAVE_TAGLIB_H
1623 auto s
= currentSong(myScreen
);
1624 myTagEditor
->LocateSong(*s
);
1625 # endif // HAVE_TAGLIB_H
1628 bool JumpToPositionInSong::canBeRun() const
1630 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
1633 void JumpToPositionInSong::run()
1635 using Global::wFooter
;
1637 const MPD::Song s
= myPlaylist
->nowPlayingSong();
1640 Statusbar::put() << "Position to go (in %/m:ss/seconds(s)): ";
1641 std::string strpos
= wFooter
->getString();
1642 Statusbar::unlock();
1647 if (boost::regex_match(strpos
, what
, rx
.assign("([0-9]+):([0-9]{2})"))) // mm:ss
1649 int mins
= fromString
<int>(what
[1]);
1650 int secs
= fromString
<int>(what
[2]);
1651 boundsCheck(secs
, 0, 60);
1652 Mpd
.Seek(s
.getPosition(), mins
* 60 + secs
);
1654 else if (boost::regex_match(strpos
, what
, rx
.assign("([0-9]+)s"))) // position in seconds
1656 int secs
= fromString
<int>(what
[1]);
1657 Mpd
.Seek(s
.getPosition(), secs
);
1659 else if (boost::regex_match(strpos
, what
, rx
.assign("([0-9]+)[%]{0,1}"))) // position in %
1661 int percent
= fromString
<int>(what
[1]);
1662 boundsCheck(percent
, 0, 100);
1663 int secs
= (percent
* s
.getDuration()) / 100.0;
1664 Mpd
.Seek(s
.getPosition(), secs
);
1667 Statusbar::print("Invalid format ([m]:[ss], [s]s, [%]%, [%] accepted)");
1670 bool ReverseSelection::canBeRun() const
1672 auto w
= hasSongs(myScreen
);
1673 return w
&& w
->allowsSelection();
1676 void ReverseSelection::run()
1678 auto w
= hasSongs(myScreen
);
1679 w
->reverseSelection();
1680 Statusbar::print("Selection reversed");
1683 bool RemoveSelection::canBeRun() const
1685 return proxySongList(myScreen
);
1688 void RemoveSelection::run()
1690 auto pl
= proxySongList(myScreen
);
1691 for (size_t i
= 0; i
< pl
.size(); ++i
)
1692 pl
.setSelected(i
, false);
1693 Statusbar::print("Selection removed");
1696 bool SelectAlbum::canBeRun() const
1698 auto w
= hasSongs(myScreen
);
1699 return w
&& w
->allowsSelection() && w
->proxySongList();
1702 void SelectAlbum::run()
1704 auto pl
= proxySongList(myScreen
);
1708 size_t pos
= pl
.choice();
1709 if (MPD::Song
*s
= pl
.getSong(pos
))
1711 std::string album
= s
->getAlbum();
1712 // select song under cursor
1713 pl
.setSelected(pos
, true);
1717 s
= pl
.getSong(--pos
);
1718 if (!s
|| s
->getAlbum() != album
)
1721 pl
.setSelected(pos
, true);
1725 while (pos
< pl
.size() - 1)
1727 s
= pl
.getSong(++pos
);
1728 if (!s
|| s
->getAlbum() != album
)
1731 pl
.setSelected(pos
, true);
1733 Statusbar::print("Album around cursor position selected");
1737 bool AddSelectedItems::canBeRun() const
1739 return myScreen
!= mySelectedItemsAdder
;
1742 void AddSelectedItems::run()
1744 mySelectedItemsAdder
->switchTo();
1747 void CropMainPlaylist::run()
1749 auto &w
= myPlaylist
->main();
1750 // cropping doesn't make sense in this case
1754 if (Config
.ask_before_clearing_playlists
)
1755 yes
= askYesNoQuestion("Do you really want to crop main playlist?", Status::trace
);
1758 Statusbar::print("Cropping playlist...");
1759 selectCurrentIfNoneSelected(w
);
1760 cropPlaylist(w
, boost::bind(&MPD::Connection::Delete
, _1
, _2
));
1761 Statusbar::print("Playlist cropped");
1765 bool CropPlaylist::canBeRun() const
1767 return myScreen
== myPlaylistEditor
;
1770 void CropPlaylist::run()
1772 auto &w
= myPlaylistEditor
->Content
;
1773 // cropping doesn't make sense in this case
1776 assert(!myPlaylistEditor
->Playlists
.empty());
1777 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
1779 if (Config
.ask_before_clearing_playlists
)
1780 yes
= askYesNoQuestion(
1781 boost::format("Do you really want to crop playlist \"%1%\"?") % playlist
,
1786 selectCurrentIfNoneSelected(w
);
1787 Statusbar::printf("Cropping playlist \"%1%\"...", playlist
);
1788 cropPlaylist(w
, boost::bind(&MPD::Connection::PlaylistDelete
, _1
, playlist
, _2
));
1789 Statusbar::printf("Playlist \"%1%\" cropped", playlist
);
1793 void ClearMainPlaylist::run()
1796 if (Config
.ask_before_clearing_playlists
)
1797 yes
= askYesNoQuestion("Do you really want to clear main playlist?", Status::trace
);
1800 auto delete_fun
= boost::bind(&MPD::Connection::Delete
, _1
, _2
);
1801 auto clear_fun
= boost::bind(&MPD::Connection::ClearMainPlaylist
, _1
);
1802 Statusbar::printf("Deleting items...");
1803 clearPlaylist(myPlaylist
->main(), delete_fun
, clear_fun
);
1804 Statusbar::printf("Items deleted");
1805 myPlaylist
->main().reset();
1809 bool ClearPlaylist::canBeRun() const
1811 return myScreen
== myPlaylistEditor
;
1814 void ClearPlaylist::run()
1816 assert(!myPlaylistEditor
->Playlists
.empty());
1817 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
1819 if (Config
.ask_before_clearing_playlists
)
1820 yes
= askYesNoQuestion(
1821 boost::format("Do you really want to clear playlist \"%1%\"?") % playlist
,
1826 auto delete_fun
= boost::bind(&MPD::Connection::PlaylistDelete
, _1
, playlist
, _2
);
1827 auto clear_fun
= boost::bind(&MPD::Connection::ClearPlaylist
, _1
, playlist
);
1828 Statusbar::printf("Deleting items from \"%1%\"...", playlist
);
1829 clearPlaylist(myPlaylistEditor
->Content
, delete_fun
, clear_fun
);
1830 Statusbar::printf("Items deleted from \"%1%\"", playlist
);
1834 bool SortPlaylist::canBeRun() const
1836 return myScreen
== myPlaylist
;
1839 void SortPlaylist::run()
1841 mySortPlaylistDialog
->switchTo();
1844 bool ReversePlaylist::canBeRun() const
1846 return myScreen
== myPlaylist
;
1849 void ReversePlaylist::run()
1851 myPlaylist
->Reverse();
1854 bool ApplyFilter::canBeRun() const
1856 auto w
= dynamic_cast<Filterable
*>(myScreen
);
1857 return w
&& w
->allowsFiltering();
1860 void ApplyFilter::run()
1862 using Global::wFooter
;
1864 Filterable
*f
= dynamic_cast<Filterable
*>(myScreen
);
1865 std::string filter
= f
->currentFilter();
1866 // if filter is already here, apply it
1867 if (!filter
.empty())
1869 f
->applyFilter(filter
);
1870 myScreen
->refreshWindow();
1874 Statusbar::put() << NC::Format::Bold
<< "Apply filter: " << NC::Format::NoBold
;
1875 wFooter
->setGetStringHelper(Statusbar::Helpers::ApplyFilterImmediately(f
, filter
));
1876 wFooter
->getString(filter
);
1877 wFooter
->setGetStringHelper(Statusbar::Helpers::getString
);
1878 Statusbar::unlock();
1880 filter
= f
->currentFilter();
1883 myPlaylist
->main().clearFilterResults();
1884 Statusbar::printf("Filtering disabled");
1888 // apply filter here so even if old one wasn't modified
1889 // (and callback wasn't invoked), it still gets applied.
1890 f
->applyFilter(filter
);
1891 Statusbar::printf("Using filter \"%1%\"", filter
);
1894 if (myScreen
== myPlaylist
)
1896 myPlaylist
->EnableHighlighting();
1897 myPlaylist
->reloadTotalLength();
1900 listsChangeFinisher();
1903 bool Find::canBeRun() const
1905 return myScreen
== myHelp
1906 || myScreen
== myLyrics
1907 # ifdef HAVE_CURL_CURL_H
1908 || myScreen
== myLastfm
1909 # endif // HAVE_CURL_CURL_H
1915 using Global::wFooter
;
1918 Statusbar::put() << "Find: ";
1919 std::string findme
= wFooter
->getString();
1920 Statusbar::unlock();
1922 Statusbar::print("Searching...");
1923 auto s
= static_cast<Screen
<NC::Scrollpad
> *>(myScreen
);
1924 s
->main().removeProperties();
1925 if (findme
.empty() || s
->main().setProperties(NC::Format::Reverse
, findme
, NC::Format::NoReverse
))
1926 Statusbar::print("Done");
1928 Statusbar::print("No matching patterns found");
1932 bool FindItemBackward::canBeRun() const
1934 auto w
= dynamic_cast<Searchable
*>(myScreen
);
1935 return w
&& w
->allowsSearching();
1938 void FindItemForward::run()
1940 findItem(::Find::Forward
);
1941 listsChangeFinisher();
1944 bool FindItemForward::canBeRun() const
1946 auto w
= dynamic_cast<Searchable
*>(myScreen
);
1947 return w
&& w
->allowsSearching();
1950 void FindItemBackward::run()
1952 findItem(::Find::Backward
);
1953 listsChangeFinisher();
1956 bool NextFoundItem::canBeRun() const
1958 return dynamic_cast<Searchable
*>(myScreen
);
1961 void NextFoundItem::run()
1963 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
1964 w
->nextFound(Config
.wrapped_search
);
1965 listsChangeFinisher();
1968 bool PreviousFoundItem::canBeRun() const
1970 return dynamic_cast<Searchable
*>(myScreen
);
1973 void PreviousFoundItem::run()
1975 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
1976 w
->prevFound(Config
.wrapped_search
);
1977 listsChangeFinisher();
1980 void ToggleFindMode::run()
1982 Config
.wrapped_search
= !Config
.wrapped_search
;
1983 Statusbar::printf("Search mode: %1%",
1984 Config
.wrapped_search
? "Wrapped" : "Normal"
1988 void ToggleReplayGainMode::run()
1990 using Global::wFooter
;
1993 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]";
1999 answer
= wFooter
->readKey();
2001 while (answer
!= 'o' && answer
!= 't' && answer
!= 'a');
2002 Statusbar::unlock();
2003 Mpd
.SetReplayGainMode(answer
== 't' ? MPD::rgmTrack
: (answer
== 'a' ? MPD::rgmAlbum
: MPD::rgmOff
));
2004 Statusbar::printf("Replay gain mode: %1%", Mpd
.GetReplayGainMode());
2007 void ToggleSpaceMode::run()
2009 Config
.space_selects
= !Config
.space_selects
;
2010 Statusbar::printf("Space mode: %1% item", Config
.space_selects
? "select" : "add");
2013 void ToggleAddMode::run()
2015 std::string mode_desc
;
2016 switch (Config
.space_add_mode
)
2018 case SpaceAddMode::AddRemove
:
2019 Config
.space_add_mode
= SpaceAddMode::AlwaysAdd
;
2020 mode_desc
= "always add an item to playlist";
2022 case SpaceAddMode::AlwaysAdd
:
2023 Config
.space_add_mode
= SpaceAddMode::AddRemove
;
2024 mode_desc
= "add an item to playlist or remove if already added";
2027 Statusbar::printf("Add mode: %1%", mode_desc
);
2030 void ToggleMouse::run()
2032 Config
.mouse_support
= !Config
.mouse_support
;
2033 mousemask(Config
.mouse_support
? ALL_MOUSE_EVENTS
: 0, 0);
2034 Statusbar::printf("Mouse support %1%",
2035 Config
.mouse_support
? "enabled" : "disabled"
2039 void ToggleBitrateVisibility::run()
2041 Config
.display_bitrate
= !Config
.display_bitrate
;
2042 Statusbar::printf("Bitrate visibility %1%",
2043 Config
.display_bitrate
? "enabled" : "disabled"
2047 void AddRandomItems::run()
2049 using Global::wFooter
;
2052 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] ";
2058 answer
= wFooter
->readKey();
2060 while (answer
!= 's' && answer
!= 'a' && answer
!= 'b');
2061 Statusbar::unlock();
2063 mpd_tag_type tag_type
= MPD_TAG_ARTIST
;
2064 std::string tag_type_str
;
2067 tag_type
= charToTagType(answer
);
2068 tag_type_str
= boost::locale::to_lower(tagTypeToString(tag_type
));
2071 tag_type_str
= "song";
2074 Statusbar::put() << "Number of random " << tag_type_str
<< "s: ";
2075 std::string strnum
= wFooter
->getString();
2076 Statusbar::unlock();
2077 size_t number
= fromString
<size_t>(strnum
);
2078 if (number
&& (answer
== 's' ? Mpd
.AddRandomSongs(number
) : Mpd
.AddRandomTag(tag_type
, number
)))
2080 Statusbar::printf("%1% random %2%%3% added to playlist",
2081 number
, tag_type_str
, number
== 1 ? "" : "s"
2086 bool ToggleBrowserSortMode::canBeRun() const
2088 return myScreen
== myBrowser
;
2091 void ToggleBrowserSortMode::run()
2093 switch (Config
.browser_sort_mode
)
2095 case SortMode::Name
:
2096 Config
.browser_sort_mode
= SortMode::ModificationTime
;
2097 Statusbar::print("Sort songs by: modification time");
2099 case SortMode::ModificationTime
:
2100 Config
.browser_sort_mode
= SortMode::CustomFormat
;
2101 Statusbar::print("Sort songs by: custom format");
2103 case SortMode::CustomFormat
:
2104 Config
.browser_sort_mode
= SortMode::NoOp
;
2105 Statusbar::print("Do not sort songs");
2107 case SortMode::NoOp
:
2108 Config
.browser_sort_mode
= SortMode::Name
;
2109 Statusbar::print("Sort songs by: name");
2111 withUnfilteredMenuReapplyFilter(myBrowser
->main(), [] {
2112 if (Config
.browser_sort_mode
!= SortMode::NoOp
)
2113 std::sort(myBrowser
->main().begin()+(myBrowser
->CurrentDir() != "/"), myBrowser
->main().end(),
2114 LocaleBasedItemSorting(std::locale(), Config
.ignore_leading_the
, Config
.browser_sort_mode
)
2119 bool ToggleLibraryTagType::canBeRun() const
2121 return (myScreen
->isActiveWindow(myLibrary
->Tags
))
2122 || (myLibrary
->Columns() == 2 && myScreen
->isActiveWindow(myLibrary
->Albums
));
2125 void ToggleLibraryTagType::run()
2127 using Global::wFooter
;
2130 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] ";
2136 answer
= wFooter
->readKey();
2138 while (answer
!= 'a' && answer
!= 'A' && answer
!= 'y' && answer
!= 'g' && answer
!= 'c' && answer
!= 'p');
2139 Statusbar::unlock();
2140 mpd_tag_type new_tagitem
= charToTagType(answer
);
2141 if (new_tagitem
!= Config
.media_lib_primary_tag
)
2143 Config
.media_lib_primary_tag
= new_tagitem
;
2144 std::string item_type
= tagTypeToString(Config
.media_lib_primary_tag
);
2145 myLibrary
->Tags
.setTitle(Config
.titles_visibility
? item_type
+ "s" : "");
2146 myLibrary
->Tags
.reset();
2147 item_type
= boost::locale::to_lower(item_type
);
2148 std::string and_mtime
= Config
.media_library_sort_by_mtime
?
2151 if (myLibrary
->Columns() == 2)
2153 myLibrary
->Songs
.clear();
2154 myLibrary
->Albums
.reset();
2155 myLibrary
->Albums
.clear();
2156 myLibrary
->Albums
.setTitle(Config
.titles_visibility
? "Albums (sorted by " + item_type
+ and_mtime
+ ")" : "");
2157 myLibrary
->Albums
.display();
2161 myLibrary
->Tags
.clear();
2162 myLibrary
->Tags
.display();
2164 Statusbar::printf("Switched to the list of %1%s", item_type
);
2168 bool ToggleMediaLibrarySortMode::canBeRun() const
2170 return myScreen
== myLibrary
;
2173 void ToggleMediaLibrarySortMode::run()
2175 myLibrary
->toggleSortMode();
2178 bool RefetchLyrics::canBeRun() const
2180 # ifdef HAVE_CURL_CURL_H
2181 return myScreen
== myLyrics
;
2184 # endif // HAVE_CURL_CURL_H
2187 void RefetchLyrics::run()
2189 # ifdef HAVE_CURL_CURL_H
2190 myLyrics
->Refetch();
2191 # endif // HAVE_CURL_CURL_H
2194 bool SetSelectedItemsPriority::canBeRun() const
2196 if (Mpd
.Version() < 17)
2198 Statusbar::print("Priorities are supported in MPD >= 0.17.0");
2201 return myScreen
== myPlaylist
&& !myPlaylist
->main().empty();
2204 void SetSelectedItemsPriority::run()
2206 using Global::wFooter
;
2209 Statusbar::put() << "Set priority [0-255]: ";
2210 std::string strprio
= wFooter
->getString();
2211 Statusbar::unlock();
2212 unsigned prio
= fromString
<unsigned>(strprio
);
2213 boundsCheck(prio
, 0u, 255u);
2214 myPlaylist
->SetSelectedItemsPriority(prio
);
2217 bool SetVisualizerSampleMultiplier::canBeRun() const
2219 # ifdef ENABLE_VISUALIZER
2220 return myScreen
== myVisualizer
;
2223 # endif // ENABLE_VISUALIZER
2226 void SetVisualizerSampleMultiplier::run()
2228 # ifdef ENABLE_VISUALIZER
2229 using Global::wFooter
;
2232 Statusbar::put() << "Set visualizer sample multiplier: ";
2233 std::string smultiplier
= wFooter
->getString();
2234 Statusbar::unlock();
2236 double multiplier
= fromString
<double>(smultiplier
);
2237 lowerBoundCheck(multiplier
, 0.0);
2238 Config
.visualizer_sample_multiplier
= multiplier
;
2239 # endif // ENABLE_VISUALIZER
2242 bool FilterPlaylistOnPriorities::canBeRun() const
2244 return myScreen
== myPlaylist
;
2247 void FilterPlaylistOnPriorities::run()
2249 using Global::wFooter
;
2252 Statusbar::put() << "Show songs with priority higher than: ";
2253 std::string strprio
= wFooter
->getString();
2254 Statusbar::unlock();
2255 unsigned prio
= fromString
<unsigned>(strprio
);
2256 boundsCheck(prio
, 0u, 255u);
2257 myPlaylist
->main().filter(myPlaylist
->main().begin(), myPlaylist
->main().end(),
2258 [prio
](const NC::Menu
<MPD::Song
>::Item
&s
) {
2259 return s
.value().getPrio() > prio
;
2261 Statusbar::printf("Playlist filtered (songs with priority higher than %1%)", prio
);
2264 void ShowSongInfo::run()
2266 mySongInfo
->switchTo();
2269 bool ShowArtistInfo::canBeRun() const
2271 #ifdef HAVE_CURL_CURL_H
2272 return myScreen
== myLastfm
2273 || (myScreen
->isActiveWindow(myLibrary
->Tags
)
2274 && !myLibrary
->Tags
.empty()
2275 && Config
.media_lib_primary_tag
== MPD_TAG_ARTIST
)
2276 || currentSong(myScreen
);
2279 # endif // NOT HAVE_CURL_CURL_H
2282 void ShowArtistInfo::run()
2284 # ifdef HAVE_CURL_CURL_H
2285 if (myScreen
== myLastfm
)
2287 myLastfm
->switchTo();
2292 if (myScreen
->isActiveWindow(myLibrary
->Tags
))
2294 assert(!myLibrary
->Tags
.empty());
2295 assert(Config
.media_lib_primary_tag
== MPD_TAG_ARTIST
);
2296 artist
= myLibrary
->Tags
.current().value().tag();
2300 auto s
= currentSong(myScreen
);
2302 artist
= s
->getArtist();
2305 if (!artist
.empty())
2307 myLastfm
->queueJob(LastFm::ArtistInfo(artist
, Config
.lastfm_preferred_language
));
2308 myLastfm
->switchTo();
2310 # endif // HAVE_CURL_CURL_H
2313 void ShowLyrics::run()
2315 myLyrics
->switchTo();
2320 ExitMainLoop
= true;
2323 void NextScreen::run()
2325 if (Config
.screen_switcher_previous
)
2327 if (auto tababble
= dynamic_cast<Tabbable
*>(myScreen
))
2328 tababble
->switchToPreviousScreen();
2330 else if (!Config
.screen_sequence
.empty())
2332 const auto &seq
= Config
.screen_sequence
;
2333 auto screen_type
= std::find(seq
.begin(), seq
.end(), myScreen
->type());
2334 if (++screen_type
== seq
.end())
2335 toScreen(seq
.front())->switchTo();
2337 toScreen(*screen_type
)->switchTo();
2341 void PreviousScreen::run()
2343 if (Config
.screen_switcher_previous
)
2345 if (auto tababble
= dynamic_cast<Tabbable
*>(myScreen
))
2346 tababble
->switchToPreviousScreen();
2348 else if (!Config
.screen_sequence
.empty())
2350 const auto &seq
= Config
.screen_sequence
;
2351 auto screen_type
= std::find(seq
.begin(), seq
.end(), myScreen
->type());
2352 if (screen_type
== seq
.begin())
2353 toScreen(seq
.back())->switchTo();
2355 toScreen(*--screen_type
)->switchTo();
2359 bool ShowHelp::canBeRun() const
2361 return myScreen
!= myHelp
2362 # ifdef HAVE_TAGLIB_H
2363 && myScreen
!= myTinyTagEditor
2364 # endif // HAVE_TAGLIB_H
2368 void ShowHelp::run()
2373 bool ShowPlaylist::canBeRun() const
2375 return myScreen
!= myPlaylist
2376 # ifdef HAVE_TAGLIB_H
2377 && myScreen
!= myTinyTagEditor
2378 # endif // HAVE_TAGLIB_H
2382 void ShowPlaylist::run()
2384 myPlaylist
->switchTo();
2387 bool ShowBrowser::canBeRun() const
2389 return myScreen
!= myBrowser
2390 # ifdef HAVE_TAGLIB_H
2391 && myScreen
!= myTinyTagEditor
2392 # endif // HAVE_TAGLIB_H
2396 void ShowBrowser::run()
2398 myBrowser
->switchTo();
2401 bool ChangeBrowseMode::canBeRun() const
2403 return myScreen
== myBrowser
;
2406 void ChangeBrowseMode::run()
2408 myBrowser
->ChangeBrowseMode();
2411 bool ShowSearchEngine::canBeRun() const
2413 return myScreen
!= mySearcher
2414 # ifdef HAVE_TAGLIB_H
2415 && myScreen
!= myTinyTagEditor
2416 # endif // HAVE_TAGLIB_H
2420 void ShowSearchEngine::run()
2422 mySearcher
->switchTo();
2425 bool ResetSearchEngine::canBeRun() const
2427 return myScreen
== mySearcher
;
2430 void ResetSearchEngine::run()
2432 mySearcher
->reset();
2435 bool ShowMediaLibrary::canBeRun() const
2437 return myScreen
!= myLibrary
2438 # ifdef HAVE_TAGLIB_H
2439 && myScreen
!= myTinyTagEditor
2440 # endif // HAVE_TAGLIB_H
2444 void ShowMediaLibrary::run()
2446 myLibrary
->switchTo();
2449 bool ToggleMediaLibraryColumnsMode::canBeRun() const
2451 return myScreen
== myLibrary
;
2454 void ToggleMediaLibraryColumnsMode::run()
2456 myLibrary
->toggleColumnsMode();
2457 myLibrary
->refresh();
2460 bool ShowPlaylistEditor::canBeRun() const
2462 return myScreen
!= myPlaylistEditor
2463 # ifdef HAVE_TAGLIB_H
2464 && myScreen
!= myTinyTagEditor
2465 # endif // HAVE_TAGLIB_H
2469 void ShowPlaylistEditor::run()
2471 myPlaylistEditor
->switchTo();
2474 bool ShowTagEditor::canBeRun() const
2476 # ifdef HAVE_TAGLIB_H
2477 return myScreen
!= myTagEditor
2478 && myScreen
!= myTinyTagEditor
;
2481 # endif // HAVE_TAGLIB_H
2484 void ShowTagEditor::run()
2486 # ifdef HAVE_TAGLIB_H
2487 if (isMPDMusicDirSet())
2488 myTagEditor
->switchTo();
2489 # endif // HAVE_TAGLIB_H
2492 bool ShowOutputs::canBeRun() const
2494 # ifdef ENABLE_OUTPUTS
2495 return myScreen
!= myOutputs
2496 # ifdef HAVE_TAGLIB_H
2497 && myScreen
!= myTinyTagEditor
2498 # endif // HAVE_TAGLIB_H
2502 # endif // ENABLE_OUTPUTS
2505 void ShowOutputs::run()
2507 # ifdef ENABLE_OUTPUTS
2508 myOutputs
->switchTo();
2509 # endif // ENABLE_OUTPUTS
2512 bool ShowVisualizer::canBeRun() const
2514 # ifdef ENABLE_VISUALIZER
2515 return myScreen
!= myVisualizer
2516 # ifdef HAVE_TAGLIB_H
2517 && myScreen
!= myTinyTagEditor
2518 # endif // HAVE_TAGLIB_H
2522 # endif // ENABLE_VISUALIZER
2525 void ShowVisualizer::run()
2527 # ifdef ENABLE_VISUALIZER
2528 myVisualizer
->switchTo();
2529 # endif // ENABLE_VISUALIZER
2532 bool ShowClock::canBeRun() const
2534 # ifdef ENABLE_CLOCK
2535 return myScreen
!= myClock
2536 # ifdef HAVE_TAGLIB_H
2537 && myScreen
!= myTinyTagEditor
2538 # endif // HAVE_TAGLIB_H
2542 # endif // ENABLE_CLOCK
2545 void ShowClock::run()
2547 # ifdef ENABLE_CLOCK
2548 myClock
->switchTo();
2549 # endif // ENABLE_CLOCK
2552 #ifdef HAVE_TAGLIB_H
2553 bool ShowServerInfo::canBeRun() const
2555 return myScreen
!= myTinyTagEditor
;
2557 #endif // HAVE_TAGLIB_H
2559 void ShowServerInfo::run()
2561 myServerInfo
->switchTo();
2568 void populateActions()
2570 auto insert_action
= [](Actions::BaseAction
*a
) {
2571 AvailableActions
[static_cast<size_t>(a
->type())] = a
;
2573 insert_action(new Actions::Dummy());
2574 insert_action(new Actions::MouseEvent());
2575 insert_action(new Actions::ScrollUp());
2576 insert_action(new Actions::ScrollDown());
2577 insert_action(new Actions::ScrollUpArtist());
2578 insert_action(new Actions::ScrollUpAlbum());
2579 insert_action(new Actions::ScrollDownArtist());
2580 insert_action(new Actions::ScrollDownAlbum());
2581 insert_action(new Actions::PageUp());
2582 insert_action(new Actions::PageDown());
2583 insert_action(new Actions::MoveHome());
2584 insert_action(new Actions::MoveEnd());
2585 insert_action(new Actions::ToggleInterface());
2586 insert_action(new Actions::JumpToParentDirectory());
2587 insert_action(new Actions::PressEnter());
2588 insert_action(new Actions::PressSpace());
2589 insert_action(new Actions::PreviousColumn());
2590 insert_action(new Actions::NextColumn());
2591 insert_action(new Actions::MasterScreen());
2592 insert_action(new Actions::SlaveScreen());
2593 insert_action(new Actions::VolumeUp());
2594 insert_action(new Actions::VolumeDown());
2595 insert_action(new Actions::DeletePlaylistItems());
2596 insert_action(new Actions::DeleteStoredPlaylist());
2597 insert_action(new Actions::DeleteBrowserItems());
2598 insert_action(new Actions::ReplaySong());
2599 insert_action(new Actions::PreviousSong());
2600 insert_action(new Actions::NextSong());
2601 insert_action(new Actions::Pause());
2602 insert_action(new Actions::Stop());
2603 insert_action(new Actions::ExecuteCommand());
2604 insert_action(new Actions::SavePlaylist());
2605 insert_action(new Actions::MoveSortOrderUp());
2606 insert_action(new Actions::MoveSortOrderDown());
2607 insert_action(new Actions::MoveSelectedItemsUp());
2608 insert_action(new Actions::MoveSelectedItemsDown());
2609 insert_action(new Actions::MoveSelectedItemsTo());
2610 insert_action(new Actions::Add());
2611 insert_action(new Actions::SeekForward());
2612 insert_action(new Actions::SeekBackward());
2613 insert_action(new Actions::ToggleDisplayMode());
2614 insert_action(new Actions::ToggleSeparatorsBetweenAlbums());
2615 insert_action(new Actions::ToggleLyricsFetcher());
2616 insert_action(new Actions::ToggleFetchingLyricsInBackground());
2617 insert_action(new Actions::TogglePlayingSongCentering());
2618 insert_action(new Actions::UpdateDatabase());
2619 insert_action(new Actions::JumpToPlayingSong());
2620 insert_action(new Actions::ToggleRepeat());
2621 insert_action(new Actions::Shuffle());
2622 insert_action(new Actions::ToggleRandom());
2623 insert_action(new Actions::StartSearching());
2624 insert_action(new Actions::SaveTagChanges());
2625 insert_action(new Actions::ToggleSingle());
2626 insert_action(new Actions::ToggleConsume());
2627 insert_action(new Actions::ToggleCrossfade());
2628 insert_action(new Actions::SetCrossfade());
2629 insert_action(new Actions::SetVolume());
2630 insert_action(new Actions::EditSong());
2631 insert_action(new Actions::EditLibraryTag());
2632 insert_action(new Actions::EditLibraryAlbum());
2633 insert_action(new Actions::EditDirectoryName());
2634 insert_action(new Actions::EditPlaylistName());
2635 insert_action(new Actions::EditLyrics());
2636 insert_action(new Actions::JumpToBrowser());
2637 insert_action(new Actions::JumpToMediaLibrary());
2638 insert_action(new Actions::JumpToPlaylistEditor());
2639 insert_action(new Actions::ToggleScreenLock());
2640 insert_action(new Actions::JumpToTagEditor());
2641 insert_action(new Actions::JumpToPositionInSong());
2642 insert_action(new Actions::ReverseSelection());
2643 insert_action(new Actions::RemoveSelection());
2644 insert_action(new Actions::SelectAlbum());
2645 insert_action(new Actions::AddSelectedItems());
2646 insert_action(new Actions::CropMainPlaylist());
2647 insert_action(new Actions::CropPlaylist());
2648 insert_action(new Actions::ClearMainPlaylist());
2649 insert_action(new Actions::ClearPlaylist());
2650 insert_action(new Actions::SortPlaylist());
2651 insert_action(new Actions::ReversePlaylist());
2652 insert_action(new Actions::ApplyFilter());
2653 insert_action(new Actions::Find());
2654 insert_action(new Actions::FindItemForward());
2655 insert_action(new Actions::FindItemBackward());
2656 insert_action(new Actions::NextFoundItem());
2657 insert_action(new Actions::PreviousFoundItem());
2658 insert_action(new Actions::ToggleFindMode());
2659 insert_action(new Actions::ToggleReplayGainMode());
2660 insert_action(new Actions::ToggleSpaceMode());
2661 insert_action(new Actions::ToggleAddMode());
2662 insert_action(new Actions::ToggleMouse());
2663 insert_action(new Actions::ToggleBitrateVisibility());
2664 insert_action(new Actions::AddRandomItems());
2665 insert_action(new Actions::ToggleBrowserSortMode());
2666 insert_action(new Actions::ToggleLibraryTagType());
2667 insert_action(new Actions::ToggleMediaLibrarySortMode());
2668 insert_action(new Actions::RefetchLyrics());
2669 insert_action(new Actions::SetSelectedItemsPriority());
2670 insert_action(new Actions::SetVisualizerSampleMultiplier());
2671 insert_action(new Actions::FilterPlaylistOnPriorities());
2672 insert_action(new Actions::ShowSongInfo());
2673 insert_action(new Actions::ShowArtistInfo());
2674 insert_action(new Actions::ShowLyrics());
2675 insert_action(new Actions::Quit());
2676 insert_action(new Actions::NextScreen());
2677 insert_action(new Actions::PreviousScreen());
2678 insert_action(new Actions::ShowHelp());
2679 insert_action(new Actions::ShowPlaylist());
2680 insert_action(new Actions::ShowBrowser());
2681 insert_action(new Actions::ChangeBrowseMode());
2682 insert_action(new Actions::ShowSearchEngine());
2683 insert_action(new Actions::ResetSearchEngine());
2684 insert_action(new Actions::ShowMediaLibrary());
2685 insert_action(new Actions::ToggleMediaLibraryColumnsMode());
2686 insert_action(new Actions::ShowPlaylistEditor());
2687 insert_action(new Actions::ShowTagEditor());
2688 insert_action(new Actions::ShowOutputs());
2689 insert_action(new Actions::ShowVisualizer());
2690 insert_action(new Actions::ShowClock());
2691 insert_action(new Actions::ShowServerInfo());
2696 using Global::wHeader
;
2697 using Global::wFooter
;
2698 using Global::Timer
;
2699 using Global::SeekingInProgress
;
2701 if (!Status::State::totalTime())
2703 Statusbar::print("Unknown item length");
2707 Progressbar::lock();
2710 unsigned songpos
= Status::State::elapsedTime();
2713 int old_timeout
= wFooter
->getTimeout();
2714 wFooter
->setTimeout(500);
2716 auto seekForward
= &Actions::get(Actions::Type::SeekForward
);
2717 auto seekBackward
= &Actions::get(Actions::Type::SeekBackward
);
2719 SeekingInProgress
= true;
2724 unsigned howmuch
= Config
.incremental_seeking
2725 ? (Timer
-t
).seconds()/2+Config
.seek_time
2728 Key input
= Key::read(*wFooter
);
2729 auto k
= Bindings
.get(input
);
2730 if (k
.first
== k
.second
|| !k
.first
->isSingle()) // no single action?
2732 auto a
= k
.first
->action();
2733 if (a
== seekForward
)
2735 if (songpos
< Status::State::totalTime())
2736 songpos
= std::min(songpos
+ howmuch
, Status::State::totalTime());
2738 else if (a
== seekBackward
)
2742 if (songpos
< howmuch
)
2751 *wFooter
<< NC::Format::Bold
;
2752 std::string tracklength
;
2753 switch (Config
.design
)
2755 case Design::Classic
:
2757 if (Config
.display_remaining_time
)
2760 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime()-songpos
);
2763 tracklength
+= MPD::Song::ShowTime(songpos
);
2765 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime());
2767 *wFooter
<< NC::XY(wFooter
->getWidth()-tracklength
.length(), 1) << tracklength
;
2769 case Design::Alternative
:
2770 if (Config
.display_remaining_time
)
2773 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime()-songpos
);
2776 tracklength
= MPD::Song::ShowTime(songpos
);
2778 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime());
2779 *wHeader
<< NC::XY(0, 0) << tracklength
<< " ";
2783 *wFooter
<< NC::Format::NoBold
;
2784 Progressbar::draw(songpos
, Status::State::totalTime());
2787 SeekingInProgress
= false;
2788 Mpd
.Seek(Status::State::currentSongPosition(), songpos
);
2790 wFooter
->setTimeout(old_timeout
);
2792 Progressbar::unlock();
2793 Statusbar::unlock();
2796 void findItem(const Find direction
)
2798 using Global::wFooter
;
2800 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
2802 assert(w
->allowsSearching());
2805 Statusbar::put() << "Find " << (direction
== Find::Forward
? "forward" : "backward") << ": ";
2806 std::string findme
= wFooter
->getString();
2807 Statusbar::unlock();
2809 if (!findme
.empty())
2810 Statusbar::print("Searching...");
2812 bool success
= w
->search(findme
);
2818 Statusbar::print("Searching finished");
2820 Statusbar::printf("Unable to find \"%1%\"", findme
);
2822 if (direction
== ::Find::Forward
)
2823 w
->nextFound(Config
.wrapped_search
);
2825 w
->prevFound(Config
.wrapped_search
);
2827 if (myScreen
== myPlaylist
)
2828 myPlaylist
->EnableHighlighting();
2831 void listsChangeFinisher()
2833 if (myScreen
== myLibrary
2834 || myScreen
== myPlaylistEditor
2835 # ifdef HAVE_TAGLIB_H
2836 || myScreen
== myTagEditor
2837 # endif // HAVE_TAGLIB_H
2840 if (myScreen
->activeWindow() == &myLibrary
->Tags
)
2842 myLibrary
->Albums
.clear();
2843 myLibrary
->Albums
.refresh();
2844 myLibrary
->Songs
.clear();
2845 myLibrary
->Songs
.refresh();
2846 myLibrary
->updateTimer();
2848 else if (myScreen
->activeWindow() == &myLibrary
->Albums
)
2850 myLibrary
->Songs
.clear();
2851 myLibrary
->Songs
.refresh();
2852 myLibrary
->updateTimer();
2854 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
))
2856 myPlaylistEditor
->Content
.clear();
2857 myPlaylistEditor
->Content
.refresh();
2858 myPlaylistEditor
->updateTimer();
2860 # ifdef HAVE_TAGLIB_H
2861 else if (myScreen
->activeWindow() == myTagEditor
->Dirs
)
2863 myTagEditor
->Tags
->clear();
2865 # endif // HAVE_TAGLIB_H