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
);
383 size_t pos
= pl
.choice();
384 if (MPD::Song
*s
= pl
.getSong(pos
))
386 std::string artist
= s
->getArtist();
389 s
= pl
.getSong(--pos
);
390 if (!s
|| s
->getArtist() != artist
)
397 bool ScrollUpAlbum::canBeRun() const
399 return proxySongList(myScreen
);
402 void ScrollUpAlbum::run()
404 auto pl
= proxySongList(myScreen
);
406 size_t pos
= pl
.choice();
407 if (MPD::Song
*s
= pl
.getSong(pos
))
409 std::string album
= s
->getAlbum();
412 s
= pl
.getSong(--pos
);
413 if (!s
|| s
->getAlbum() != album
)
420 bool ScrollDownArtist::canBeRun() const
422 return proxySongList(myScreen
);
425 void ScrollDownArtist::run()
427 auto pl
= proxySongList(myScreen
);
429 size_t pos
= pl
.choice();
430 if (MPD::Song
*s
= pl
.getSong(pos
))
432 std::string artist
= s
->getArtist();
433 while (pos
< pl
.size() - 1)
435 s
= pl
.getSong(++pos
);
436 if (!s
|| s
->getArtist() != artist
)
443 bool ScrollDownAlbum::canBeRun() const
445 return proxySongList(myScreen
);
448 void ScrollDownAlbum::run()
450 auto pl
= proxySongList(myScreen
);
452 size_t pos
= pl
.choice();
453 if (MPD::Song
*s
= pl
.getSong(pos
))
455 std::string album
= s
->getAlbum();
456 while (pos
< pl
.size() - 1)
458 s
= pl
.getSong(++pos
);
459 if (!s
|| s
->getAlbum() != album
)
468 myScreen
->scroll(NC::Scroll::PageUp
);
469 listsChangeFinisher();
474 myScreen
->scroll(NC::Scroll::PageDown
);
475 listsChangeFinisher();
480 myScreen
->scroll(NC::Scroll::Home
);
481 listsChangeFinisher();
486 myScreen
->scroll(NC::Scroll::End
);
487 listsChangeFinisher();
490 void ToggleInterface::run()
492 switch (Config
.design
)
494 case Design::Classic
:
495 Config
.design
= Design::Alternative
;
496 Config
.statusbar_visibility
= false;
498 case Design::Alternative
:
499 Config
.design
= Design::Classic
;
500 Config
.statusbar_visibility
= OriginalStatusbarVisibility
;
503 setWindowsDimensions();
504 Progressbar::unlock();
507 Status::Changes::mixer();
508 Status::Changes::elapsedTime(false);
509 Statusbar::printf("User interface: %1%", Config
.design
);
512 bool JumpToParentDirectory::canBeRun() const
514 return (myScreen
== myBrowser
)
515 # ifdef HAVE_TAGLIB_H
516 || (myScreen
->activeWindow() == myTagEditor
->Dirs
)
517 # endif // HAVE_TAGLIB_H
521 void JumpToParentDirectory::run()
523 if (myScreen
== myBrowser
)
525 if (myBrowser
->CurrentDir() != "/")
527 myBrowser
->main().reset();
528 myBrowser
->enterPressed();
531 # ifdef HAVE_TAGLIB_H
532 else if (myScreen
== myTagEditor
)
534 if (myTagEditor
->CurrentDir() != "/")
536 myTagEditor
->Dirs
->reset();
537 myTagEditor
->enterPressed();
540 # endif // HAVE_TAGLIB_H
543 void PressEnter::run()
545 myScreen
->enterPressed();
548 void PressSpace::run()
550 myScreen
->spacePressed();
553 bool PreviousColumn::canBeRun() const
555 auto hc
= hasColumns(myScreen
);
556 return hc
&& hc
->previousColumnAvailable();
559 void PreviousColumn::run()
561 hasColumns(myScreen
)->previousColumn();
564 bool NextColumn::canBeRun() const
566 auto hc
= hasColumns(myScreen
);
567 return hc
&& hc
->nextColumnAvailable();
570 void NextColumn::run()
572 hasColumns(myScreen
)->nextColumn();
575 bool MasterScreen::canBeRun() const
577 using Global::myLockedScreen
;
578 using Global::myInactiveScreen
;
580 return myLockedScreen
582 && myLockedScreen
!= myScreen
583 && myScreen
->isMergable();
586 void MasterScreen::run()
588 using Global::myInactiveScreen
;
589 using Global::myLockedScreen
;
591 myInactiveScreen
= myScreen
;
592 myScreen
= myLockedScreen
;
596 bool SlaveScreen::canBeRun() const
598 using Global::myLockedScreen
;
599 using Global::myInactiveScreen
;
601 return myLockedScreen
603 && myLockedScreen
== myScreen
604 && myScreen
->isMergable();
607 void SlaveScreen::run()
609 using Global::myInactiveScreen
;
610 using Global::myLockedScreen
;
612 myScreen
= myInactiveScreen
;
613 myInactiveScreen
= myLockedScreen
;
619 int volume
= std::min(Status::State::volume()+Config
.volume_change_step
, 100u);
620 Mpd
.SetVolume(volume
);
623 void VolumeDown::run()
625 int volume
= std::max(int(Status::State::volume()-Config
.volume_change_step
), 0);
626 Mpd
.SetVolume(volume
);
629 bool DeletePlaylistItems::canBeRun() const
631 return (myScreen
== myPlaylist
&& !myPlaylist
->main().empty())
632 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
) && !myPlaylistEditor
->Content
.empty());
635 void DeletePlaylistItems::run()
637 if (myScreen
== myPlaylist
)
639 Statusbar::print("Deleting items...");
640 auto delete_fun
= boost::bind(&MPD::Connection::Delete
, _1
, _2
);
641 deleteSelectedSongs(myPlaylist
->main(), delete_fun
);
642 Statusbar::print("Item(s) deleted");
644 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Content
))
646 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
647 auto delete_fun
= boost::bind(&MPD::Connection::PlaylistDelete
, _1
, playlist
, _2
);
648 Statusbar::print("Deleting items...");
649 deleteSelectedSongs(myPlaylistEditor
->Content
, delete_fun
);
650 Statusbar::print("Item(s) deleted");
654 bool DeleteBrowserItems::canBeRun() const
656 auto check_if_deletion_allowed
= []() {
657 if (Config
.allow_for_physical_item_deletion
)
661 Statusbar::print("Flag \"allow_for_physical_item_deletion\" needs to be enabled in configuration file");
665 return myScreen
== myBrowser
666 && !myBrowser
->main().empty()
667 && isMPDMusicDirSet()
668 && check_if_deletion_allowed();
671 void DeleteBrowserItems::run()
673 boost::format question
;
674 if (hasSelected(myBrowser
->main().begin(), myBrowser
->main().end()))
675 question
= boost::format("Delete selected items?");
678 MPD::Item
&item
= myBrowser
->main().current().value();
679 std::string iname
= item
.type
== MPD::itSong
? item
.song
->getName() : item
.name
;
680 question
= boost::format("Delete %1% \"%2%\"?")
681 % itemTypeToString(item
.type
) % wideShorten(iname
, COLS
-question
.size()-10);
683 bool yes
= askYesNoQuestion(question
, Status::trace
);
687 auto list
= getSelectedOrCurrent(myBrowser
->main().begin(), myBrowser
->main().end(), myBrowser
->main().currentI());
688 for (auto it
= list
.begin(); it
!= list
.end(); ++it
)
690 const MPD::Item
&i
= (*it
)->value();
691 std::string iname
= i
.type
== MPD::itSong
? i
.song
->getName() : i
.name
;
693 if (myBrowser
->deleteItem(i
, errmsg
))
695 const char msg
[] = "\"%1%\" deleted";
696 Statusbar::printf(msg
, wideShorten(iname
, COLS
-const_strlen(msg
)));
700 Statusbar::print(errmsg
);
707 if (myBrowser
->isLocal())
708 myBrowser
->GetDirectory(myBrowser
->CurrentDir());
710 Mpd
.UpdateDirectory(myBrowser
->CurrentDir());
714 Statusbar::print("Aborted");
717 bool DeleteStoredPlaylist::canBeRun() const
719 return myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
)
720 && myPlaylistEditor
->Playlists
.empty();
723 void DeleteStoredPlaylist::run()
725 boost::format question
;
726 if (hasSelected(myPlaylistEditor
->Playlists
.begin(), myPlaylistEditor
->Playlists
.end()))
727 question
= boost::format("Delete selected playlists?");
729 question
= boost::format("Delete playlist \"%1%\"?")
730 % wideShorten(myPlaylistEditor
->Playlists
.current().value(), COLS
-question
.size()-10);
731 bool yes
= askYesNoQuestion(question
, Status::trace
);
734 auto list
= getSelectedOrCurrent(myPlaylistEditor
->Playlists
.begin(), myPlaylistEditor
->Playlists
.end(), myPlaylistEditor
->Playlists
.currentI());
735 Mpd
.StartCommandsList();
736 for (auto it
= list
.begin(); it
!= list
.end(); ++it
)
737 Mpd
.DeletePlaylist((*it
)->value());
738 Mpd
.CommitCommandsList();
739 Statusbar::printf("%1% deleted", list
.size() == 1 ? "Playlist" : "Playlists");
742 Statusbar::print("Aborted");
745 void ReplaySong::run()
747 if (Status::State::player() != MPD::psStop
)
748 Mpd
.Seek(Status::State::currentSongPosition(), 0);
751 void PreviousSong::run()
766 void SavePlaylist::run()
768 using Global::wFooter
;
771 Statusbar::put() << "Save playlist as: ";
772 std::string playlist_name
= wFooter
->getString();
774 if (playlist_name
.find("/") != std::string::npos
)
776 Statusbar::print("Playlist name must not contain slashes");
779 if (!playlist_name
.empty())
781 if (myPlaylist
->main().isFiltered())
783 Mpd
.StartCommandsList();
784 for (size_t i
= 0; i
< myPlaylist
->main().size(); ++i
)
785 Mpd
.AddToPlaylist(playlist_name
, myPlaylist
->main()[i
].value());
786 Mpd
.CommitCommandsList();
787 Statusbar::printf("Filtered items added to playlist \"%1%\"", playlist_name
);
793 Mpd
.SavePlaylist(playlist_name
);
794 Statusbar::printf("Playlist saved as \"%1%\"", playlist_name
);
796 catch (MPD::ServerError
&e
)
798 if (e
.code() == MPD_SERVER_ERROR_EXIST
)
800 bool yes
= askYesNoQuestion(
801 boost::format("Playlist \"%1%\" already exists, overwrite?") % playlist_name
,
806 Mpd
.DeletePlaylist(playlist_name
);
807 Mpd
.SavePlaylist(playlist_name
);
808 Statusbar::print("Playlist overwritten");
811 Statusbar::print("Aborted");
812 if (myScreen
== myPlaylist
)
813 myPlaylist
->EnableHighlighting();
820 if (!myBrowser
->isLocal()
821 && myBrowser
->CurrentDir() == "/"
822 && !myBrowser
->main().empty())
823 myBrowser
->GetDirectory(myBrowser
->CurrentDir());
831 void ExecuteCommand::run()
833 using Global::wFooter
;
835 Statusbar::put() << NC::Format::Bold
<< ":" << NC::Format::NoBold
;
836 wFooter
->setGetStringHelper(Statusbar::Helpers::TryExecuteImmediateCommand());
837 std::string cmd_name
= wFooter
->getString();
838 wFooter
->setGetStringHelper(Statusbar::Helpers::getString
);
840 if (cmd_name
.empty())
842 auto cmd
= Bindings
.findCommand(cmd_name
);
845 Statusbar::printf(1, "Executing %1%...", cmd_name
);
846 bool res
= cmd
->binding().execute();
847 Statusbar::printf("Execution of command \"%1%\" %2%.",
848 cmd_name
, res
? "successful" : "unsuccessful"
852 Statusbar::printf("No command named \"%1%\"", cmd_name
);
855 bool MoveSortOrderUp::canBeRun() const
857 return myScreen
== mySortPlaylistDialog
;
860 void MoveSortOrderUp::run()
862 mySortPlaylistDialog
->moveSortOrderUp();
865 bool MoveSortOrderDown::canBeRun() const
867 return myScreen
== mySortPlaylistDialog
;
870 void MoveSortOrderDown::run()
872 mySortPlaylistDialog
->moveSortOrderDown();
875 bool MoveSelectedItemsUp::canBeRun() const
877 return ((myScreen
== myPlaylist
878 && !myPlaylist
->main().empty()
879 && !myPlaylist
->isFiltered())
880 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
)
881 && !myPlaylistEditor
->Content
.empty()
882 && !myPlaylistEditor
->isContentFiltered()));
885 void MoveSelectedItemsUp::run()
887 if (myScreen
== myPlaylist
)
889 moveSelectedItemsUp(myPlaylist
->main(), boost::bind(&MPD::Connection::Move
, _1
, _2
, _3
));
891 else if (myScreen
== myPlaylistEditor
)
893 assert(!myPlaylistEditor
->Playlists
.empty());
894 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
895 auto move_fun
= boost::bind(&MPD::Connection::PlaylistMove
, _1
, playlist
, _2
, _3
);
896 moveSelectedItemsUp(myPlaylistEditor
->Content
, move_fun
);
900 bool MoveSelectedItemsDown::canBeRun() const
902 return ((myScreen
== myPlaylist
903 && !myPlaylist
->main().empty()
904 && !myPlaylist
->isFiltered())
905 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
)
906 && !myPlaylistEditor
->Content
.empty()
907 && !myPlaylistEditor
->isContentFiltered()));
910 void MoveSelectedItemsDown::run()
912 if (myScreen
== myPlaylist
)
914 moveSelectedItemsDown(myPlaylist
->main(), boost::bind(&MPD::Connection::Move
, _1
, _2
, _3
));
916 else if (myScreen
== myPlaylistEditor
)
918 assert(!myPlaylistEditor
->Playlists
.empty());
919 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
920 auto move_fun
= boost::bind(&MPD::Connection::PlaylistMove
, _1
, playlist
, _2
, _3
);
921 moveSelectedItemsDown(myPlaylistEditor
->Content
, move_fun
);
925 bool MoveSelectedItemsTo::canBeRun() const
927 return myScreen
== myPlaylist
928 || myScreen
->isActiveWindow(myPlaylistEditor
->Content
);
931 void MoveSelectedItemsTo::run()
933 if (myScreen
== myPlaylist
)
934 moveSelectedItemsTo(myPlaylist
->main(), boost::bind(&MPD::Connection::Move
, _1
, _2
, _3
));
937 assert(!myPlaylistEditor
->Playlists
.empty());
938 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
939 auto move_fun
= boost::bind(&MPD::Connection::PlaylistMove
, _1
, playlist
, _2
, _3
);
940 moveSelectedItemsTo(myPlaylistEditor
->Content
, move_fun
);
944 bool Add::canBeRun() const
946 return myScreen
!= myPlaylistEditor
947 || !myPlaylistEditor
->Playlists
.empty();
952 using Global::wFooter
;
955 Statusbar::put() << (myScreen
== myPlaylistEditor
? "Add to playlist: " : "Add: ");
956 std::string path
= wFooter
->getString();
960 Statusbar::put() << "Adding...";
962 if (myScreen
== myPlaylistEditor
)
963 Mpd
.AddToPlaylist(myPlaylistEditor
->Playlists
.current().value(), path
);
966 const char lastfm_url
[] = "lastfm://";
967 if (path
.compare(0, const_strlen(lastfm_url
), lastfm_url
) == 0
968 || path
.find(".asx", path
.length()-4) != std::string::npos
969 || path
.find(".cue", path
.length()-4) != std::string::npos
970 || path
.find(".m3u", path
.length()-4) != std::string::npos
971 || path
.find(".pls", path
.length()-4) != std::string::npos
972 || path
.find(".xspf", path
.length()-5) != std::string::npos
)
973 Mpd
.LoadPlaylist(path
);
980 bool SeekForward::canBeRun() const
982 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
985 void SeekForward::run()
990 bool SeekBackward::canBeRun() const
992 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
995 void SeekBackward::run()
1000 bool ToggleDisplayMode::canBeRun() const
1002 return myScreen
== myPlaylist
1003 || myScreen
== myBrowser
1004 || myScreen
== mySearcher
1005 || myScreen
->isActiveWindow(myPlaylistEditor
->Content
);
1008 void ToggleDisplayMode::run()
1010 if (myScreen
== myPlaylist
)
1012 switch (Config
.playlist_display_mode
)
1014 case DisplayMode::Classic
:
1015 Config
.playlist_display_mode
= DisplayMode::Columns
;
1016 myPlaylist
->main().setItemDisplayer(boost::bind(
1017 Display::SongsInColumns
, _1
, myPlaylist
->proxySongList()
1019 if (Config
.titles_visibility
)
1020 myPlaylist
->main().setTitle(Display::Columns(myPlaylist
->main().getWidth()));
1022 myPlaylist
->main().setTitle("");
1024 case DisplayMode::Columns
:
1025 Config
.playlist_display_mode
= DisplayMode::Classic
;
1026 myPlaylist
->main().setItemDisplayer(boost::bind(
1027 Display::Songs
, _1
, myPlaylist
->proxySongList(), Config
.song_list_format
1029 myPlaylist
->main().setTitle("");
1031 Statusbar::printf("Playlist display mode: %1%", Config
.playlist_display_mode
);
1033 else if (myScreen
== myBrowser
)
1035 switch (Config
.browser_display_mode
)
1037 case DisplayMode::Classic
:
1038 Config
.browser_display_mode
= DisplayMode::Columns
;
1039 if (Config
.titles_visibility
)
1040 myBrowser
->main().setTitle(Display::Columns(myBrowser
->main().getWidth()));
1042 myBrowser
->main().setTitle("");
1044 case DisplayMode::Columns
:
1045 Config
.browser_display_mode
= DisplayMode::Classic
;
1046 myBrowser
->main().setTitle("");
1049 Statusbar::printf("Browser display mode: %1%", Config
.browser_display_mode
);
1051 else if (myScreen
== mySearcher
)
1053 switch (Config
.search_engine_display_mode
)
1055 case DisplayMode::Classic
:
1056 Config
.search_engine_display_mode
= DisplayMode::Columns
;
1058 case DisplayMode::Columns
:
1059 Config
.search_engine_display_mode
= DisplayMode::Classic
;
1062 Statusbar::printf("Search engine display mode: %1%", Config
.search_engine_display_mode
);
1063 if (mySearcher
->main().size() > SearchEngine::StaticOptions
)
1064 mySearcher
->main().setTitle(
1065 Config
.search_engine_display_mode
== DisplayMode::Columns
1066 && Config
.titles_visibility
1067 ? Display::Columns(mySearcher
->main().getWidth())
1071 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Content
))
1073 switch (Config
.playlist_editor_display_mode
)
1075 case DisplayMode::Classic
:
1076 Config
.playlist_editor_display_mode
= DisplayMode::Columns
;
1077 myPlaylistEditor
->Content
.setItemDisplayer(boost::bind(
1078 Display::SongsInColumns
, _1
, myPlaylistEditor
->contentProxyList()
1081 case DisplayMode::Columns
:
1082 Config
.playlist_editor_display_mode
= DisplayMode::Classic
;
1083 myPlaylistEditor
->Content
.setItemDisplayer(boost::bind(
1084 Display::Songs
, _1
, myPlaylistEditor
->contentProxyList(), Config
.song_list_format
1088 Statusbar::printf("Playlist editor display mode: %1%", Config
.playlist_editor_display_mode
);
1092 bool ToggleSeparatorsBetweenAlbums::canBeRun() const
1097 void ToggleSeparatorsBetweenAlbums::run()
1099 Config
.playlist_separate_albums
= !Config
.playlist_separate_albums
;
1100 Statusbar::printf("Separators between albums: %1%",
1101 Config
.playlist_separate_albums
? "on" : "off"
1105 #ifndef HAVE_CURL_CURL_H
1106 bool ToggleLyricsFetcher::canBeRun() const
1110 #endif // NOT HAVE_CURL_CURL_H
1112 void ToggleLyricsFetcher::run()
1114 # ifdef HAVE_CURL_CURL_H
1115 myLyrics
->ToggleFetcher();
1116 # endif // HAVE_CURL_CURL_H
1119 #ifndef HAVE_CURL_CURL_H
1120 bool ToggleFetchingLyricsInBackground::canBeRun() const
1124 #endif // NOT HAVE_CURL_CURL_H
1126 void ToggleFetchingLyricsInBackground::run()
1128 # ifdef HAVE_CURL_CURL_H
1129 Config
.fetch_lyrics_in_background
= !Config
.fetch_lyrics_in_background
;
1130 Statusbar::printf("Fetching lyrics for playing songs in background: %1%",
1131 Config
.fetch_lyrics_in_background
? "on" : "off"
1133 # endif // HAVE_CURL_CURL_H
1136 void TogglePlayingSongCentering::run()
1138 Config
.autocenter_mode
= !Config
.autocenter_mode
;
1139 Statusbar::printf("Centering playing song: %1%",
1140 Config
.autocenter_mode
? "on" : "off"
1142 if (Config
.autocenter_mode
1143 && Status::State::player() != MPD::psUnknown
1144 && !myPlaylist
->main().isFiltered())
1145 myPlaylist
->main().highlight(Status::State::currentSongPosition());
1148 void UpdateDatabase::run()
1150 if (myScreen
== myBrowser
)
1151 Mpd
.UpdateDirectory(myBrowser
->CurrentDir());
1152 # ifdef HAVE_TAGLIB_H
1153 else if (myScreen
== myTagEditor
)
1154 Mpd
.UpdateDirectory(myTagEditor
->CurrentDir());
1155 # endif // HAVE_TAGLIB_H
1157 Mpd
.UpdateDirectory("/");
1160 bool JumpToPlayingSong::canBeRun() const
1162 return ((myScreen
== myPlaylist
&& !myPlaylist
->isFiltered())
1163 || myScreen
== myBrowser
1164 || myScreen
== myLibrary
)
1165 && Status::State::player() != MPD::psUnknown
;
1168 void JumpToPlayingSong::run()
1170 if (myScreen
== myPlaylist
)
1171 myPlaylist
->main().highlight(Status::State::currentSongPosition());
1172 else if (myScreen
== myBrowser
)
1174 myBrowser
->LocateSong(myPlaylist
->nowPlayingSong());
1177 else if (myScreen
== myLibrary
)
1179 myLibrary
->LocateSong(myPlaylist
->nowPlayingSong());
1183 void ToggleRepeat::run()
1185 Mpd
.SetRepeat(!Status::State::repeat());
1193 void ToggleRandom::run()
1195 Mpd
.SetRandom(!Status::State::random());
1198 bool StartSearching::canBeRun() const
1200 return myScreen
== mySearcher
&& !mySearcher
->main()[0].isInactive();
1203 void StartSearching::run()
1205 mySearcher
->main().highlight(SearchEngine::SearchButton
);
1206 mySearcher
->main().setHighlighting(0);
1207 mySearcher
->main().refresh();
1208 mySearcher
->main().setHighlighting(1);
1209 mySearcher
->enterPressed();
1212 bool SaveTagChanges::canBeRun() const
1214 # ifdef HAVE_TAGLIB_H
1215 return myScreen
== myTinyTagEditor
1216 || myScreen
->activeWindow() == myTagEditor
->TagTypes
;
1219 # endif // HAVE_TAGLIB_H
1222 void SaveTagChanges::run()
1224 # ifdef HAVE_TAGLIB_H
1225 if (myScreen
== myTinyTagEditor
)
1227 myTinyTagEditor
->main().highlight(myTinyTagEditor
->main().size()-2); // Save
1228 myTinyTagEditor
->enterPressed();
1230 else if (myScreen
->activeWindow() == myTagEditor
->TagTypes
)
1232 myTagEditor
->TagTypes
->highlight(myTagEditor
->TagTypes
->size()-1); // Save
1233 myTagEditor
->enterPressed();
1235 # endif // HAVE_TAGLIB_H
1238 void ToggleSingle::run()
1240 Mpd
.SetSingle(!Status::State::single());
1243 void ToggleConsume::run()
1245 Mpd
.SetConsume(!Status::State::consume());
1248 void ToggleCrossfade::run()
1250 Mpd
.SetCrossfade(Status::State::crossfade() ? 0 : Config
.crossfade_time
);
1253 void SetCrossfade::run()
1255 using Global::wFooter
;
1258 Statusbar::put() << "Set crossfade to: ";
1259 std::string crossfade
= wFooter
->getString();
1260 Statusbar::unlock();
1261 int cf
= fromString
<unsigned>(crossfade
);
1262 lowerBoundCheck(cf
, 1);
1263 Config
.crossfade_time
= cf
;
1264 Mpd
.SetCrossfade(cf
);
1267 void SetVolume::run()
1269 using Global::wFooter
;
1272 Statusbar::put() << "Set volume to: ";
1273 std::string strvolume
= wFooter
->getString();
1274 Statusbar::unlock();
1275 int volume
= fromString
<unsigned>(strvolume
);
1276 boundsCheck(volume
, 0, 100);
1277 Mpd
.SetVolume(volume
);
1278 Statusbar::printf("Volume set to %1%%%", volume
);
1281 bool EditSong::canBeRun() const
1283 # ifdef HAVE_TAGLIB_H
1284 return currentSong(myScreen
)
1285 && isMPDMusicDirSet();
1288 # endif // HAVE_TAGLIB_H
1291 void EditSong::run()
1293 # ifdef HAVE_TAGLIB_H
1294 auto s
= currentSong(myScreen
);
1295 myTinyTagEditor
->SetEdited(*s
);
1296 myTinyTagEditor
->switchTo();
1297 # endif // HAVE_TAGLIB_H
1300 bool EditLibraryTag::canBeRun() const
1302 # ifdef HAVE_TAGLIB_H
1303 return myScreen
->isActiveWindow(myLibrary
->Tags
)
1304 && !myLibrary
->Tags
.empty()
1305 && isMPDMusicDirSet();
1308 # endif // HAVE_TAGLIB_H
1311 void EditLibraryTag::run()
1313 # ifdef HAVE_TAGLIB_H
1314 using Global::wFooter
;
1317 Statusbar::put() << NC::Format::Bold
<< tagTypeToString(Config
.media_lib_primary_tag
) << NC::Format::NoBold
<< ": ";
1318 std::string new_tag
= wFooter
->getString(myLibrary
->Tags
.current().value().tag());
1319 Statusbar::unlock();
1320 if (!new_tag
.empty() && new_tag
!= myLibrary
->Tags
.current().value().tag())
1322 Statusbar::print("Updating tags...");
1324 Mpd
.AddSearch(Config
.media_lib_primary_tag
, myLibrary
->Tags
.current().value().tag());
1325 MPD::MutableSong::SetFunction set
= tagTypeToSetFunction(Config
.media_lib_primary_tag
);
1327 bool success
= true;
1328 std::string dir_to_update
;
1329 Mpd
.CommitSearchSongs([set
, &dir_to_update
, &new_tag
, &success
](MPD::Song s
) {
1332 MPD::MutableSong ms
= s
;
1333 ms
.setTags(set
, new_tag
, Config
.tags_separator
);
1334 Statusbar::printf("Updating tags in \"%1%\"...", ms
.getName());
1335 std::string path
= Config
.mpd_music_dir
+ ms
.getURI();
1336 if (!Tags::write(ms
))
1338 const char msg
[] = "Error while updating tags in \"%1%\"";
1339 Statusbar::printf(msg
, wideShorten(ms
.getURI(), COLS
-const_strlen(msg
)));
1342 if (dir_to_update
.empty())
1343 dir_to_update
= s
.getURI();
1345 dir_to_update
= getSharedDirectory(dir_to_update
, s
.getURI());
1349 Mpd
.UpdateDirectory(dir_to_update
);
1350 Statusbar::print("Tags updated successfully");
1353 # endif // HAVE_TAGLIB_H
1356 bool EditLibraryAlbum::canBeRun() const
1358 # ifdef HAVE_TAGLIB_H
1359 return myScreen
->isActiveWindow(myLibrary
->Albums
)
1360 && !myLibrary
->Albums
.empty()
1361 && isMPDMusicDirSet();
1364 # endif // HAVE_TAGLIB_H
1367 void EditLibraryAlbum::run()
1369 # ifdef HAVE_TAGLIB_H
1370 using Global::wFooter
;
1373 Statusbar::put() << NC::Format::Bold
<< "Album: " << NC::Format::NoBold
;
1374 std::string new_album
= wFooter
->getString(myLibrary
->Albums
.current().value().entry().album());
1375 Statusbar::unlock();
1376 if (!new_album
.empty() && new_album
!= myLibrary
->Albums
.current().value().entry().album())
1379 Statusbar::print("Updating tags...");
1380 for (size_t i
= 0; i
< myLibrary
->Songs
.size(); ++i
)
1382 Statusbar::printf("Updating tags in \"%1%\"...", myLibrary
->Songs
[i
].value().getName());
1383 std::string path
= Config
.mpd_music_dir
+ myLibrary
->Songs
[i
].value().getURI();
1384 TagLib::FileRef
f(path
.c_str());
1387 const char msg
[] = "Error while opening file \"%1%\"";
1388 Statusbar::printf(msg
, wideShorten(myLibrary
->Songs
[i
].value().getURI(), COLS
-const_strlen(msg
)));
1392 f
.tag()->setAlbum(ToWString(new_album
));
1395 const char msg
[] = "Error while writing tags in \"%1%\"";
1396 Statusbar::printf(msg
, wideShorten(myLibrary
->Songs
[i
].value().getURI(), COLS
-const_strlen(msg
)));
1403 Mpd
.UpdateDirectory(getSharedDirectory(myLibrary
->Songs
.beginV(), myLibrary
->Songs
.endV()));
1404 Statusbar::print("Tags updated successfully");
1407 # endif // HAVE_TAGLIB_H
1410 bool EditDirectoryName::canBeRun() const
1412 return ((myScreen
== myBrowser
1413 && !myBrowser
->main().empty()
1414 && myBrowser
->main().current().value().type
== MPD::itDirectory
)
1415 # ifdef HAVE_TAGLIB_H
1416 || (myScreen
->activeWindow() == myTagEditor
->Dirs
1417 && !myTagEditor
->Dirs
->empty()
1418 && myTagEditor
->Dirs
->choice() > 0)
1419 # endif // HAVE_TAGLIB_H
1420 ) && isMPDMusicDirSet();
1423 void EditDirectoryName::run()
1425 using Global::wFooter
;
1427 if (myScreen
== myBrowser
)
1429 std::string old_dir
= myBrowser
->main().current().value().name
;
1431 Statusbar::put() << NC::Format::Bold
<< "Directory: " << NC::Format::NoBold
;
1432 std::string new_dir
= wFooter
->getString(old_dir
);
1433 Statusbar::unlock();
1434 if (!new_dir
.empty() && new_dir
!= old_dir
)
1436 std::string full_old_dir
;
1437 if (!myBrowser
->isLocal())
1438 full_old_dir
+= Config
.mpd_music_dir
;
1439 full_old_dir
+= old_dir
;
1440 std::string full_new_dir
;
1441 if (!myBrowser
->isLocal())
1442 full_new_dir
+= Config
.mpd_music_dir
;
1443 full_new_dir
+= new_dir
;
1444 int rename_result
= rename(full_old_dir
.c_str(), full_new_dir
.c_str());
1445 if (rename_result
== 0)
1447 const char msg
[] = "Directory renamed to \"%1%\"";
1448 Statusbar::printf(msg
, wideShorten(new_dir
, COLS
-const_strlen(msg
)));
1449 if (!myBrowser
->isLocal())
1450 Mpd
.UpdateDirectory(getSharedDirectory(old_dir
, new_dir
));
1451 myBrowser
->GetDirectory(myBrowser
->CurrentDir());
1455 const char msg
[] = "Couldn't rename \"%1%\": %s";
1456 Statusbar::printf(msg
, wideShorten(old_dir
, COLS
-const_strlen(msg
)-25), strerror(errno
));
1460 # ifdef HAVE_TAGLIB_H
1461 else if (myScreen
->activeWindow() == myTagEditor
->Dirs
)
1463 std::string old_dir
= myTagEditor
->Dirs
->current().value().first
;
1465 Statusbar::put() << NC::Format::Bold
<< "Directory: " << NC::Format::NoBold
;
1466 std::string new_dir
= wFooter
->getString(old_dir
);
1467 Statusbar::unlock();
1468 if (!new_dir
.empty() && new_dir
!= old_dir
)
1470 std::string full_old_dir
= Config
.mpd_music_dir
+ myTagEditor
->CurrentDir() + "/" + old_dir
;
1471 std::string full_new_dir
= Config
.mpd_music_dir
+ myTagEditor
->CurrentDir() + "/" + new_dir
;
1472 if (rename(full_old_dir
.c_str(), full_new_dir
.c_str()) == 0)
1474 const char msg
[] = "Directory renamed to \"%1%\"";
1475 Statusbar::printf(msg
, wideShorten(new_dir
, COLS
-const_strlen(msg
)));
1476 Mpd
.UpdateDirectory(myTagEditor
->CurrentDir());
1480 const char msg
[] = "Couldn't rename \"%1%\": %2%";
1481 Statusbar::printf(msg
, wideShorten(old_dir
, COLS
-const_strlen(msg
)-25), strerror(errno
));
1485 # endif // HAVE_TAGLIB_H
1488 bool EditPlaylistName::canBeRun() const
1490 return (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
)
1491 && !myPlaylistEditor
->Playlists
.empty())
1492 || (myScreen
== myBrowser
1493 && !myBrowser
->main().empty()
1494 && myBrowser
->main().current().value().type
== MPD::itPlaylist
);
1497 void EditPlaylistName::run()
1499 using Global::wFooter
;
1501 std::string old_name
;
1502 if (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
))
1503 old_name
= myPlaylistEditor
->Playlists
.current().value();
1505 old_name
= myBrowser
->main().current().value().name
;
1507 Statusbar::put() << NC::Format::Bold
<< "Playlist: " << NC::Format::NoBold
;
1508 std::string new_name
= wFooter
->getString(old_name
);
1509 Statusbar::unlock();
1510 if (!new_name
.empty() && new_name
!= old_name
)
1512 Mpd
.Rename(old_name
, new_name
);
1513 const char msg
[] = "Playlist renamed to \"%1%\"";
1514 Statusbar::printf(msg
, wideShorten(new_name
, COLS
-const_strlen(msg
)));
1515 if (!myBrowser
->isLocal())
1516 myBrowser
->GetDirectory("/");
1520 bool EditLyrics::canBeRun() const
1522 return myScreen
== myLyrics
;
1525 void EditLyrics::run()
1530 bool JumpToBrowser::canBeRun() const
1532 return currentSong(myScreen
);
1535 void JumpToBrowser::run()
1537 auto s
= currentSong(myScreen
);
1538 myBrowser
->LocateSong(*s
);
1541 bool JumpToMediaLibrary::canBeRun() const
1543 return currentSong(myScreen
);
1546 void JumpToMediaLibrary::run()
1548 auto s
= currentSong(myScreen
);
1549 myLibrary
->LocateSong(*s
);
1552 bool JumpToPlaylistEditor::canBeRun() const
1554 return myScreen
== myBrowser
1555 && myBrowser
->main().current().value().type
== MPD::itPlaylist
;
1558 void JumpToPlaylistEditor::run()
1560 myPlaylistEditor
->Locate(myBrowser
->main().current().value().name
);
1563 void ToggleScreenLock::run()
1565 using Global::wFooter
;
1566 using Global::myLockedScreen
;
1568 if (myLockedScreen
!= 0)
1570 BaseScreen::unlock();
1571 Actions::setResizeFlags();
1573 Statusbar::print("Screen unlocked");
1577 int part
= Config
.locked_screen_width_part
*100;
1578 if (Config
.ask_for_locked_screen_width_part
)
1581 Statusbar::put() << "% of the locked screen's width to be reserved (20-80): ";
1582 std::string strpart
= wFooter
->getString(boost::lexical_cast
<std::string
>(part
));
1583 Statusbar::unlock();
1584 part
= fromString
<unsigned>(strpart
);
1586 boundsCheck(part
, 20, 80);
1587 Config
.locked_screen_width_part
= part
/100.0;
1588 if (myScreen
->lock())
1589 Statusbar::printf("Screen locked (with %1%%% width)", part
);
1591 Statusbar::print("Current screen can't be locked");
1595 bool JumpToTagEditor::canBeRun() const
1597 # ifdef HAVE_TAGLIB_H
1598 return currentSong(myScreen
)
1599 && isMPDMusicDirSet();
1602 # endif // HAVE_TAGLIB_H
1605 void JumpToTagEditor::run()
1607 # ifdef HAVE_TAGLIB_H
1608 auto s
= currentSong(myScreen
);
1609 myTagEditor
->LocateSong(*s
);
1610 # endif // HAVE_TAGLIB_H
1613 bool JumpToPositionInSong::canBeRun() const
1615 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
1618 void JumpToPositionInSong::run()
1620 using Global::wFooter
;
1622 const MPD::Song s
= myPlaylist
->nowPlayingSong();
1625 Statusbar::put() << "Position to go (in %/m:ss/seconds(s)): ";
1626 std::string strpos
= wFooter
->getString();
1627 Statusbar::unlock();
1632 if (boost::regex_match(strpos
, what
, rx
.assign("([0-9]+):([0-9]{2})"))) // mm:ss
1634 int mins
= fromString
<int>(what
[1]);
1635 int secs
= fromString
<int>(what
[2]);
1636 boundsCheck(secs
, 0, 60);
1637 Mpd
.Seek(s
.getPosition(), mins
* 60 + secs
);
1639 else if (boost::regex_match(strpos
, what
, rx
.assign("([0-9]+)s"))) // position in seconds
1641 int secs
= fromString
<int>(what
[1]);
1642 Mpd
.Seek(s
.getPosition(), secs
);
1644 else if (boost::regex_match(strpos
, what
, rx
.assign("([0-9]+)[%]{0,1}"))) // position in %
1646 int percent
= fromString
<int>(what
[1]);
1647 boundsCheck(percent
, 0, 100);
1648 int secs
= (percent
* s
.getDuration()) / 100.0;
1649 Mpd
.Seek(s
.getPosition(), secs
);
1652 Statusbar::print("Invalid format ([m]:[ss], [s]s, [%]%, [%] accepted)");
1655 bool ReverseSelection::canBeRun() const
1657 auto w
= hasSongs(myScreen
);
1658 return w
&& w
->allowsSelection();
1661 void ReverseSelection::run()
1663 auto w
= hasSongs(myScreen
);
1664 w
->reverseSelection();
1665 Statusbar::print("Selection reversed");
1668 bool RemoveSelection::canBeRun() const
1670 return proxySongList(myScreen
);
1673 void RemoveSelection::run()
1675 auto pl
= proxySongList(myScreen
);
1676 for (size_t i
= 0; i
< pl
.size(); ++i
)
1677 pl
.setSelected(i
, false);
1678 Statusbar::print("Selection removed");
1681 bool SelectAlbum::canBeRun() const
1683 auto w
= hasSongs(myScreen
);
1684 return w
&& w
->allowsSelection() && w
->proxySongList();
1687 void SelectAlbum::run()
1689 auto pl
= proxySongList(myScreen
);
1690 size_t pos
= pl
.choice();
1691 if (MPD::Song
*s
= pl
.getSong(pos
))
1693 std::string album
= s
->getAlbum();
1694 // select song under cursor
1695 pl
.setSelected(pos
, true);
1699 s
= pl
.getSong(--pos
);
1700 if (!s
|| s
->getAlbum() != album
)
1703 pl
.setSelected(pos
, true);
1707 while (pos
< pl
.size() - 1)
1709 s
= pl
.getSong(++pos
);
1710 if (!s
|| s
->getAlbum() != album
)
1713 pl
.setSelected(pos
, true);
1715 Statusbar::print("Album around cursor position selected");
1719 bool AddSelectedItems::canBeRun() const
1721 return myScreen
!= mySelectedItemsAdder
;
1724 void AddSelectedItems::run()
1726 mySelectedItemsAdder
->switchTo();
1729 void CropMainPlaylist::run()
1731 auto &w
= myPlaylist
->main();
1732 // cropping doesn't make sense in this case
1736 if (Config
.ask_before_clearing_playlists
)
1737 yes
= askYesNoQuestion("Do you really want to crop main playlist?", Status::trace
);
1740 Statusbar::print("Cropping playlist...");
1741 selectCurrentIfNoneSelected(w
);
1742 cropPlaylist(w
, boost::bind(&MPD::Connection::Delete
, _1
, _2
));
1743 Statusbar::print("Playlist cropped");
1747 bool CropPlaylist::canBeRun() const
1749 return myScreen
== myPlaylistEditor
;
1752 void CropPlaylist::run()
1754 auto &w
= myPlaylistEditor
->Content
;
1755 // cropping doesn't make sense in this case
1758 assert(!myPlaylistEditor
->Playlists
.empty());
1759 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
1761 if (Config
.ask_before_clearing_playlists
)
1762 yes
= askYesNoQuestion(
1763 boost::format("Do you really want to crop playlist \"%1%\"?") % playlist
,
1768 selectCurrentIfNoneSelected(w
);
1769 Statusbar::printf("Cropping playlist \"%1%\"...", playlist
);
1770 cropPlaylist(w
, boost::bind(&MPD::Connection::PlaylistDelete
, _1
, playlist
, _2
));
1771 Statusbar::printf("Playlist \"%1%\" cropped", playlist
);
1775 void ClearMainPlaylist::run()
1778 if (Config
.ask_before_clearing_playlists
)
1779 yes
= askYesNoQuestion("Do you really want to clear main playlist?", Status::trace
);
1782 auto delete_fun
= boost::bind(&MPD::Connection::Delete
, _1
, _2
);
1783 auto clear_fun
= boost::bind(&MPD::Connection::ClearMainPlaylist
, _1
);
1784 Statusbar::printf("Deleting items...");
1785 clearPlaylist(myPlaylist
->main(), delete_fun
, clear_fun
);
1786 Statusbar::printf("Items deleted");
1787 myPlaylist
->main().reset();
1791 bool ClearPlaylist::canBeRun() const
1793 return myScreen
== myPlaylistEditor
;
1796 void ClearPlaylist::run()
1798 assert(!myPlaylistEditor
->Playlists
.empty());
1799 std::string playlist
= myPlaylistEditor
->Playlists
.current().value();
1801 if (Config
.ask_before_clearing_playlists
)
1802 yes
= askYesNoQuestion(
1803 boost::format("Do you really want to clear playlist \"%1%\"?") % playlist
,
1808 auto delete_fun
= boost::bind(&MPD::Connection::PlaylistDelete
, _1
, playlist
, _2
);
1809 auto clear_fun
= boost::bind(&MPD::Connection::ClearPlaylist
, _1
, playlist
);
1810 Statusbar::printf("Deleting items from \"%1%\"...", playlist
);
1811 clearPlaylist(myPlaylistEditor
->Content
, delete_fun
, clear_fun
);
1812 Statusbar::printf("Items deleted from \"%1%\"", playlist
);
1816 bool SortPlaylist::canBeRun() const
1818 return myScreen
== myPlaylist
;
1821 void SortPlaylist::run()
1823 mySortPlaylistDialog
->switchTo();
1826 bool ReversePlaylist::canBeRun() const
1828 return myScreen
== myPlaylist
;
1831 void ReversePlaylist::run()
1833 myPlaylist
->Reverse();
1836 bool ApplyFilter::canBeRun() const
1838 auto w
= dynamic_cast<Filterable
*>(myScreen
);
1839 return w
&& w
->allowsFiltering();
1842 void ApplyFilter::run()
1844 using Global::wFooter
;
1846 Filterable
*f
= dynamic_cast<Filterable
*>(myScreen
);
1847 std::string filter
= f
->currentFilter();
1848 // if filter is already here, apply it
1849 if (!filter
.empty())
1851 f
->applyFilter(filter
);
1852 myScreen
->refreshWindow();
1856 Statusbar::put() << NC::Format::Bold
<< "Apply filter: " << NC::Format::NoBold
;
1857 wFooter
->setGetStringHelper(Statusbar::Helpers::ApplyFilterImmediately(f
, filter
));
1858 wFooter
->getString(filter
);
1859 wFooter
->setGetStringHelper(Statusbar::Helpers::getString
);
1860 Statusbar::unlock();
1862 filter
= f
->currentFilter();
1865 myPlaylist
->main().clearFilterResults();
1866 Statusbar::printf("Filtering disabled");
1870 // apply filter here so even if old one wasn't modified
1871 // (and callback wasn't invoked), it still gets applied.
1872 f
->applyFilter(filter
);
1873 Statusbar::printf("Using filter \"%1%\"", filter
);
1876 if (myScreen
== myPlaylist
)
1878 myPlaylist
->EnableHighlighting();
1879 myPlaylist
->reloadTotalLength();
1882 listsChangeFinisher();
1885 bool Find::canBeRun() const
1887 return myScreen
== myHelp
1888 || myScreen
== myLyrics
1889 # ifdef HAVE_CURL_CURL_H
1890 || myScreen
== myLastfm
1891 # endif // HAVE_CURL_CURL_H
1897 using Global::wFooter
;
1900 Statusbar::put() << "Find: ";
1901 std::string findme
= wFooter
->getString();
1902 Statusbar::unlock();
1904 Statusbar::print("Searching...");
1905 auto s
= static_cast<Screen
<NC::Scrollpad
> *>(myScreen
);
1906 s
->main().removeProperties();
1907 if (findme
.empty() || s
->main().setProperties(NC::Format::Reverse
, findme
, NC::Format::NoReverse
))
1908 Statusbar::print("Done");
1910 Statusbar::print("No matching patterns found");
1914 bool FindItemBackward::canBeRun() const
1916 auto w
= dynamic_cast<Searchable
*>(myScreen
);
1917 return w
&& w
->allowsSearching();
1920 void FindItemForward::run()
1922 findItem(::Find::Forward
);
1923 listsChangeFinisher();
1926 bool FindItemForward::canBeRun() const
1928 auto w
= dynamic_cast<Searchable
*>(myScreen
);
1929 return w
&& w
->allowsSearching();
1932 void FindItemBackward::run()
1934 findItem(::Find::Backward
);
1935 listsChangeFinisher();
1938 bool NextFoundItem::canBeRun() const
1940 return dynamic_cast<Searchable
*>(myScreen
);
1943 void NextFoundItem::run()
1945 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
1946 w
->nextFound(Config
.wrapped_search
);
1947 listsChangeFinisher();
1950 bool PreviousFoundItem::canBeRun() const
1952 return dynamic_cast<Searchable
*>(myScreen
);
1955 void PreviousFoundItem::run()
1957 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
1958 w
->prevFound(Config
.wrapped_search
);
1959 listsChangeFinisher();
1962 void ToggleFindMode::run()
1964 Config
.wrapped_search
= !Config
.wrapped_search
;
1965 Statusbar::printf("Search mode: %1%",
1966 Config
.wrapped_search
? "Wrapped" : "Normal"
1970 void ToggleReplayGainMode::run()
1972 using Global::wFooter
;
1975 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]";
1981 answer
= wFooter
->readKey();
1983 while (answer
!= 'o' && answer
!= 't' && answer
!= 'a');
1984 Statusbar::unlock();
1985 Mpd
.SetReplayGainMode(answer
== 't' ? MPD::rgmTrack
: (answer
== 'a' ? MPD::rgmAlbum
: MPD::rgmOff
));
1986 Statusbar::printf("Replay gain mode: %1%", Mpd
.GetReplayGainMode());
1989 void ToggleSpaceMode::run()
1991 Config
.space_selects
= !Config
.space_selects
;
1992 Statusbar::printf("Space mode: %1% item", Config
.space_selects
? "select" : "add");
1995 void ToggleAddMode::run()
1997 std::string mode_desc
;
1998 switch (Config
.space_add_mode
)
2000 case SpaceAddMode::AddRemove
:
2001 Config
.space_add_mode
= SpaceAddMode::AlwaysAdd
;
2002 mode_desc
= "always add an item to playlist";
2004 case SpaceAddMode::AlwaysAdd
:
2005 Config
.space_add_mode
= SpaceAddMode::AddRemove
;
2006 mode_desc
= "add an item to playlist or remove if already added";
2009 Statusbar::printf("Add mode: %1%", mode_desc
);
2012 void ToggleMouse::run()
2014 Config
.mouse_support
= !Config
.mouse_support
;
2015 mousemask(Config
.mouse_support
? ALL_MOUSE_EVENTS
: 0, 0);
2016 Statusbar::printf("Mouse support %1%",
2017 Config
.mouse_support
? "enabled" : "disabled"
2021 void ToggleBitrateVisibility::run()
2023 Config
.display_bitrate
= !Config
.display_bitrate
;
2024 Statusbar::printf("Bitrate visibility %1%",
2025 Config
.display_bitrate
? "enabled" : "disabled"
2029 void AddRandomItems::run()
2031 using Global::wFooter
;
2034 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] ";
2040 answer
= wFooter
->readKey();
2042 while (answer
!= 's' && answer
!= 'a' && answer
!= 'b');
2043 Statusbar::unlock();
2045 mpd_tag_type tag_type
= MPD_TAG_ARTIST
;
2046 std::string tag_type_str
;
2049 tag_type
= charToTagType(answer
);
2050 tag_type_str
= boost::locale::to_lower(tagTypeToString(tag_type
));
2053 tag_type_str
= "song";
2056 Statusbar::put() << "Number of random " << tag_type_str
<< "s: ";
2057 std::string strnum
= wFooter
->getString();
2058 Statusbar::unlock();
2059 size_t number
= fromString
<size_t>(strnum
);
2060 if (number
&& (answer
== 's' ? Mpd
.AddRandomSongs(number
) : Mpd
.AddRandomTag(tag_type
, number
)))
2062 Statusbar::printf("%1% random %2%%3% added to playlist",
2063 number
, tag_type_str
, number
== 1 ? "" : "s"
2068 bool ToggleBrowserSortMode::canBeRun() const
2070 return myScreen
== myBrowser
;
2073 void ToggleBrowserSortMode::run()
2075 switch (Config
.browser_sort_mode
)
2077 case SortMode::Name
:
2078 Config
.browser_sort_mode
= SortMode::ModificationTime
;
2079 Statusbar::print("Sort songs by: modification time");
2081 case SortMode::ModificationTime
:
2082 Config
.browser_sort_mode
= SortMode::CustomFormat
;
2083 Statusbar::print("Sort songs by: custom format");
2085 case SortMode::CustomFormat
:
2086 Config
.browser_sort_mode
= SortMode::NoOp
;
2087 Statusbar::print("Do not sort songs");
2089 case SortMode::NoOp
:
2090 Config
.browser_sort_mode
= SortMode::Name
;
2091 Statusbar::print("Sort songs by: name");
2093 withUnfilteredMenuReapplyFilter(myBrowser
->main(), [] {
2094 if (Config
.browser_sort_mode
!= SortMode::NoOp
)
2095 std::sort(myBrowser
->main().begin()+(myBrowser
->CurrentDir() != "/"), myBrowser
->main().end(),
2096 LocaleBasedItemSorting(std::locale(), Config
.ignore_leading_the
, Config
.browser_sort_mode
)
2101 bool ToggleLibraryTagType::canBeRun() const
2103 return (myScreen
->isActiveWindow(myLibrary
->Tags
))
2104 || (myLibrary
->Columns() == 2 && myScreen
->isActiveWindow(myLibrary
->Albums
));
2107 void ToggleLibraryTagType::run()
2109 using Global::wFooter
;
2112 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] ";
2118 answer
= wFooter
->readKey();
2120 while (answer
!= 'a' && answer
!= 'A' && answer
!= 'y' && answer
!= 'g' && answer
!= 'c' && answer
!= 'p');
2121 Statusbar::unlock();
2122 mpd_tag_type new_tagitem
= charToTagType(answer
);
2123 if (new_tagitem
!= Config
.media_lib_primary_tag
)
2125 Config
.media_lib_primary_tag
= new_tagitem
;
2126 std::string item_type
= tagTypeToString(Config
.media_lib_primary_tag
);
2127 myLibrary
->Tags
.setTitle(Config
.titles_visibility
? item_type
+ "s" : "");
2128 myLibrary
->Tags
.reset();
2129 item_type
= boost::locale::to_lower(item_type
);
2130 std::string and_mtime
= Config
.media_library_sort_by_mtime
?
2133 if (myLibrary
->Columns() == 2)
2135 myLibrary
->Songs
.clear();
2136 myLibrary
->Albums
.reset();
2137 myLibrary
->Albums
.clear();
2138 myLibrary
->Albums
.setTitle(Config
.titles_visibility
? "Albums (sorted by " + item_type
+ and_mtime
+ ")" : "");
2139 myLibrary
->Albums
.display();
2143 myLibrary
->Tags
.clear();
2144 myLibrary
->Tags
.display();
2146 Statusbar::printf("Switched to the list of %1%s", item_type
);
2150 bool ToggleMediaLibrarySortMode::canBeRun() const
2152 return myScreen
== myLibrary
;
2155 void ToggleMediaLibrarySortMode::run()
2157 myLibrary
->toggleSortMode();
2160 bool RefetchLyrics::canBeRun() const
2162 # ifdef HAVE_CURL_CURL_H
2163 return myScreen
== myLyrics
;
2166 # endif // HAVE_CURL_CURL_H
2169 void RefetchLyrics::run()
2171 # ifdef HAVE_CURL_CURL_H
2172 myLyrics
->Refetch();
2173 # endif // HAVE_CURL_CURL_H
2176 bool SetSelectedItemsPriority::canBeRun() const
2178 if (Mpd
.Version() < 17)
2180 Statusbar::print("Priorities are supported in MPD >= 0.17.0");
2183 return myScreen
== myPlaylist
&& !myPlaylist
->main().empty();
2186 void SetSelectedItemsPriority::run()
2188 using Global::wFooter
;
2191 Statusbar::put() << "Set priority [0-255]: ";
2192 std::string strprio
= wFooter
->getString();
2193 Statusbar::unlock();
2194 unsigned prio
= fromString
<unsigned>(strprio
);
2195 boundsCheck(prio
, 0u, 255u);
2196 myPlaylist
->SetSelectedItemsPriority(prio
);
2199 bool SetVisualizerSampleMultiplier::canBeRun() const
2201 # ifdef ENABLE_VISUALIZER
2202 return myScreen
== myVisualizer
;
2205 # endif // ENABLE_VISUALIZER
2208 void SetVisualizerSampleMultiplier::run()
2210 # ifdef ENABLE_VISUALIZER
2211 using Global::wFooter
;
2214 Statusbar::put() << "Set visualizer sample multiplier: ";
2215 std::string smultiplier
= wFooter
->getString();
2216 Statusbar::unlock();
2218 double multiplier
= fromString
<double>(smultiplier
);
2219 lowerBoundCheck(multiplier
, 0.0);
2220 Config
.visualizer_sample_multiplier
= multiplier
;
2221 # endif // ENABLE_VISUALIZER
2224 bool FilterPlaylistOnPriorities::canBeRun() const
2226 return myScreen
== myPlaylist
;
2229 void FilterPlaylistOnPriorities::run()
2231 using Global::wFooter
;
2234 Statusbar::put() << "Show songs with priority higher than: ";
2235 std::string strprio
= wFooter
->getString();
2236 Statusbar::unlock();
2237 unsigned prio
= fromString
<unsigned>(strprio
);
2238 boundsCheck(prio
, 0u, 255u);
2239 myPlaylist
->main().filter(myPlaylist
->main().begin(), myPlaylist
->main().end(),
2240 [prio
](const NC::Menu
<MPD::Song
>::Item
&s
) {
2241 return s
.value().getPrio() > prio
;
2243 Statusbar::printf("Playlist filtered (songs with priority higher than %1%)", prio
);
2246 void ShowSongInfo::run()
2248 mySongInfo
->switchTo();
2251 bool ShowArtistInfo::canBeRun() const
2253 #ifdef HAVE_CURL_CURL_H
2254 return myScreen
== myLastfm
2255 || (myScreen
->isActiveWindow(myLibrary
->Tags
)
2256 && !myLibrary
->Tags
.empty()
2257 && Config
.media_lib_primary_tag
== MPD_TAG_ARTIST
)
2258 || currentSong(myScreen
);
2261 # endif // NOT HAVE_CURL_CURL_H
2264 void ShowArtistInfo::run()
2266 # ifdef HAVE_CURL_CURL_H
2267 if (myScreen
== myLastfm
)
2269 myLastfm
->switchTo();
2274 if (myScreen
->isActiveWindow(myLibrary
->Tags
))
2276 assert(!myLibrary
->Tags
.empty());
2277 assert(Config
.media_lib_primary_tag
== MPD_TAG_ARTIST
);
2278 artist
= myLibrary
->Tags
.current().value().tag();
2282 auto s
= currentSong(myScreen
);
2284 artist
= s
->getArtist();
2287 if (!artist
.empty())
2289 myLastfm
->queueJob(LastFm::ArtistInfo(artist
, Config
.lastfm_preferred_language
));
2290 myLastfm
->switchTo();
2292 # endif // HAVE_CURL_CURL_H
2295 void ShowLyrics::run()
2297 myLyrics
->switchTo();
2302 ExitMainLoop
= true;
2305 void NextScreen::run()
2307 if (Config
.screen_switcher_previous
)
2309 if (auto tababble
= dynamic_cast<Tabbable
*>(myScreen
))
2310 tababble
->switchToPreviousScreen();
2312 else if (!Config
.screen_sequence
.empty())
2314 const auto &seq
= Config
.screen_sequence
;
2315 auto screen_type
= std::find(seq
.begin(), seq
.end(), myScreen
->type());
2316 if (++screen_type
== seq
.end())
2317 toScreen(seq
.front())->switchTo();
2319 toScreen(*screen_type
)->switchTo();
2323 void PreviousScreen::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
.begin())
2335 toScreen(seq
.back())->switchTo();
2337 toScreen(*--screen_type
)->switchTo();
2341 bool ShowHelp::canBeRun() const
2343 return myScreen
!= myHelp
2344 # ifdef HAVE_TAGLIB_H
2345 && myScreen
!= myTinyTagEditor
2346 # endif // HAVE_TAGLIB_H
2350 void ShowHelp::run()
2355 bool ShowPlaylist::canBeRun() const
2357 return myScreen
!= myPlaylist
2358 # ifdef HAVE_TAGLIB_H
2359 && myScreen
!= myTinyTagEditor
2360 # endif // HAVE_TAGLIB_H
2364 void ShowPlaylist::run()
2366 myPlaylist
->switchTo();
2369 bool ShowBrowser::canBeRun() const
2371 return myScreen
!= myBrowser
2372 # ifdef HAVE_TAGLIB_H
2373 && myScreen
!= myTinyTagEditor
2374 # endif // HAVE_TAGLIB_H
2378 void ShowBrowser::run()
2380 myBrowser
->switchTo();
2383 bool ChangeBrowseMode::canBeRun() const
2385 return myScreen
== myBrowser
;
2388 void ChangeBrowseMode::run()
2390 myBrowser
->ChangeBrowseMode();
2393 bool ShowSearchEngine::canBeRun() const
2395 return myScreen
!= mySearcher
2396 # ifdef HAVE_TAGLIB_H
2397 && myScreen
!= myTinyTagEditor
2398 # endif // HAVE_TAGLIB_H
2402 void ShowSearchEngine::run()
2404 mySearcher
->switchTo();
2407 bool ResetSearchEngine::canBeRun() const
2409 return myScreen
== mySearcher
;
2412 void ResetSearchEngine::run()
2414 mySearcher
->reset();
2417 bool ShowMediaLibrary::canBeRun() const
2419 return myScreen
!= myLibrary
2420 # ifdef HAVE_TAGLIB_H
2421 && myScreen
!= myTinyTagEditor
2422 # endif // HAVE_TAGLIB_H
2426 void ShowMediaLibrary::run()
2428 myLibrary
->switchTo();
2431 bool ToggleMediaLibraryColumnsMode::canBeRun() const
2433 return myScreen
== myLibrary
;
2436 void ToggleMediaLibraryColumnsMode::run()
2438 myLibrary
->toggleColumnsMode();
2439 myLibrary
->refresh();
2442 bool ShowPlaylistEditor::canBeRun() const
2444 return myScreen
!= myPlaylistEditor
2445 # ifdef HAVE_TAGLIB_H
2446 && myScreen
!= myTinyTagEditor
2447 # endif // HAVE_TAGLIB_H
2451 void ShowPlaylistEditor::run()
2453 myPlaylistEditor
->switchTo();
2456 bool ShowTagEditor::canBeRun() const
2458 # ifdef HAVE_TAGLIB_H
2459 return myScreen
!= myTagEditor
2460 && myScreen
!= myTinyTagEditor
;
2463 # endif // HAVE_TAGLIB_H
2466 void ShowTagEditor::run()
2468 # ifdef HAVE_TAGLIB_H
2469 if (isMPDMusicDirSet())
2470 myTagEditor
->switchTo();
2471 # endif // HAVE_TAGLIB_H
2474 bool ShowOutputs::canBeRun() const
2476 # ifdef ENABLE_OUTPUTS
2477 return myScreen
!= myOutputs
2478 # ifdef HAVE_TAGLIB_H
2479 && myScreen
!= myTinyTagEditor
2480 # endif // HAVE_TAGLIB_H
2484 # endif // ENABLE_OUTPUTS
2487 void ShowOutputs::run()
2489 # ifdef ENABLE_OUTPUTS
2490 myOutputs
->switchTo();
2491 # endif // ENABLE_OUTPUTS
2494 bool ShowVisualizer::canBeRun() const
2496 # ifdef ENABLE_VISUALIZER
2497 return myScreen
!= myVisualizer
2498 # ifdef HAVE_TAGLIB_H
2499 && myScreen
!= myTinyTagEditor
2500 # endif // HAVE_TAGLIB_H
2504 # endif // ENABLE_VISUALIZER
2507 void ShowVisualizer::run()
2509 # ifdef ENABLE_VISUALIZER
2510 myVisualizer
->switchTo();
2511 # endif // ENABLE_VISUALIZER
2514 bool ShowClock::canBeRun() const
2516 # ifdef ENABLE_CLOCK
2517 return myScreen
!= myClock
2518 # ifdef HAVE_TAGLIB_H
2519 && myScreen
!= myTinyTagEditor
2520 # endif // HAVE_TAGLIB_H
2524 # endif // ENABLE_CLOCK
2527 void ShowClock::run()
2529 # ifdef ENABLE_CLOCK
2530 myClock
->switchTo();
2531 # endif // ENABLE_CLOCK
2534 #ifdef HAVE_TAGLIB_H
2535 bool ShowServerInfo::canBeRun() const
2537 return myScreen
!= myTinyTagEditor
;
2539 #endif // HAVE_TAGLIB_H
2541 void ShowServerInfo::run()
2543 myServerInfo
->switchTo();
2550 void populateActions()
2552 auto insert_action
= [](Actions::BaseAction
*a
) {
2553 AvailableActions
[static_cast<size_t>(a
->type())] = a
;
2555 insert_action(new Actions::Dummy());
2556 insert_action(new Actions::MouseEvent());
2557 insert_action(new Actions::ScrollUp());
2558 insert_action(new Actions::ScrollDown());
2559 insert_action(new Actions::ScrollUpArtist());
2560 insert_action(new Actions::ScrollUpAlbum());
2561 insert_action(new Actions::ScrollDownArtist());
2562 insert_action(new Actions::ScrollDownAlbum());
2563 insert_action(new Actions::PageUp());
2564 insert_action(new Actions::PageDown());
2565 insert_action(new Actions::MoveHome());
2566 insert_action(new Actions::MoveEnd());
2567 insert_action(new Actions::ToggleInterface());
2568 insert_action(new Actions::JumpToParentDirectory());
2569 insert_action(new Actions::PressEnter());
2570 insert_action(new Actions::PressSpace());
2571 insert_action(new Actions::PreviousColumn());
2572 insert_action(new Actions::NextColumn());
2573 insert_action(new Actions::MasterScreen());
2574 insert_action(new Actions::SlaveScreen());
2575 insert_action(new Actions::VolumeUp());
2576 insert_action(new Actions::VolumeDown());
2577 insert_action(new Actions::DeletePlaylistItems());
2578 insert_action(new Actions::DeleteStoredPlaylist());
2579 insert_action(new Actions::DeleteBrowserItems());
2580 insert_action(new Actions::ReplaySong());
2581 insert_action(new Actions::PreviousSong());
2582 insert_action(new Actions::NextSong());
2583 insert_action(new Actions::Pause());
2584 insert_action(new Actions::Stop());
2585 insert_action(new Actions::ExecuteCommand());
2586 insert_action(new Actions::SavePlaylist());
2587 insert_action(new Actions::MoveSortOrderUp());
2588 insert_action(new Actions::MoveSortOrderDown());
2589 insert_action(new Actions::MoveSelectedItemsUp());
2590 insert_action(new Actions::MoveSelectedItemsDown());
2591 insert_action(new Actions::MoveSelectedItemsTo());
2592 insert_action(new Actions::Add());
2593 insert_action(new Actions::SeekForward());
2594 insert_action(new Actions::SeekBackward());
2595 insert_action(new Actions::ToggleDisplayMode());
2596 insert_action(new Actions::ToggleSeparatorsBetweenAlbums());
2597 insert_action(new Actions::ToggleLyricsFetcher());
2598 insert_action(new Actions::ToggleFetchingLyricsInBackground());
2599 insert_action(new Actions::TogglePlayingSongCentering());
2600 insert_action(new Actions::UpdateDatabase());
2601 insert_action(new Actions::JumpToPlayingSong());
2602 insert_action(new Actions::ToggleRepeat());
2603 insert_action(new Actions::Shuffle());
2604 insert_action(new Actions::ToggleRandom());
2605 insert_action(new Actions::StartSearching());
2606 insert_action(new Actions::SaveTagChanges());
2607 insert_action(new Actions::ToggleSingle());
2608 insert_action(new Actions::ToggleConsume());
2609 insert_action(new Actions::ToggleCrossfade());
2610 insert_action(new Actions::SetCrossfade());
2611 insert_action(new Actions::SetVolume());
2612 insert_action(new Actions::EditSong());
2613 insert_action(new Actions::EditLibraryTag());
2614 insert_action(new Actions::EditLibraryAlbum());
2615 insert_action(new Actions::EditDirectoryName());
2616 insert_action(new Actions::EditPlaylistName());
2617 insert_action(new Actions::EditLyrics());
2618 insert_action(new Actions::JumpToBrowser());
2619 insert_action(new Actions::JumpToMediaLibrary());
2620 insert_action(new Actions::JumpToPlaylistEditor());
2621 insert_action(new Actions::ToggleScreenLock());
2622 insert_action(new Actions::JumpToTagEditor());
2623 insert_action(new Actions::JumpToPositionInSong());
2624 insert_action(new Actions::ReverseSelection());
2625 insert_action(new Actions::RemoveSelection());
2626 insert_action(new Actions::SelectAlbum());
2627 insert_action(new Actions::AddSelectedItems());
2628 insert_action(new Actions::CropMainPlaylist());
2629 insert_action(new Actions::CropPlaylist());
2630 insert_action(new Actions::ClearMainPlaylist());
2631 insert_action(new Actions::ClearPlaylist());
2632 insert_action(new Actions::SortPlaylist());
2633 insert_action(new Actions::ReversePlaylist());
2634 insert_action(new Actions::ApplyFilter());
2635 insert_action(new Actions::Find());
2636 insert_action(new Actions::FindItemForward());
2637 insert_action(new Actions::FindItemBackward());
2638 insert_action(new Actions::NextFoundItem());
2639 insert_action(new Actions::PreviousFoundItem());
2640 insert_action(new Actions::ToggleFindMode());
2641 insert_action(new Actions::ToggleReplayGainMode());
2642 insert_action(new Actions::ToggleSpaceMode());
2643 insert_action(new Actions::ToggleAddMode());
2644 insert_action(new Actions::ToggleMouse());
2645 insert_action(new Actions::ToggleBitrateVisibility());
2646 insert_action(new Actions::AddRandomItems());
2647 insert_action(new Actions::ToggleBrowserSortMode());
2648 insert_action(new Actions::ToggleLibraryTagType());
2649 insert_action(new Actions::ToggleMediaLibrarySortMode());
2650 insert_action(new Actions::RefetchLyrics());
2651 insert_action(new Actions::SetSelectedItemsPriority());
2652 insert_action(new Actions::SetVisualizerSampleMultiplier());
2653 insert_action(new Actions::FilterPlaylistOnPriorities());
2654 insert_action(new Actions::ShowSongInfo());
2655 insert_action(new Actions::ShowArtistInfo());
2656 insert_action(new Actions::ShowLyrics());
2657 insert_action(new Actions::Quit());
2658 insert_action(new Actions::NextScreen());
2659 insert_action(new Actions::PreviousScreen());
2660 insert_action(new Actions::ShowHelp());
2661 insert_action(new Actions::ShowPlaylist());
2662 insert_action(new Actions::ShowBrowser());
2663 insert_action(new Actions::ChangeBrowseMode());
2664 insert_action(new Actions::ShowSearchEngine());
2665 insert_action(new Actions::ResetSearchEngine());
2666 insert_action(new Actions::ShowMediaLibrary());
2667 insert_action(new Actions::ToggleMediaLibraryColumnsMode());
2668 insert_action(new Actions::ShowPlaylistEditor());
2669 insert_action(new Actions::ShowTagEditor());
2670 insert_action(new Actions::ShowOutputs());
2671 insert_action(new Actions::ShowVisualizer());
2672 insert_action(new Actions::ShowClock());
2673 insert_action(new Actions::ShowServerInfo());
2678 using Global::wHeader
;
2679 using Global::wFooter
;
2680 using Global::Timer
;
2681 using Global::SeekingInProgress
;
2683 if (!Status::State::totalTime())
2685 Statusbar::print("Unknown item length");
2689 Progressbar::lock();
2692 unsigned songpos
= Status::State::elapsedTime();
2695 int old_timeout
= wFooter
->getTimeout();
2696 wFooter
->setTimeout(500);
2698 auto seekForward
= &Actions::get(Actions::Type::SeekForward
);
2699 auto seekBackward
= &Actions::get(Actions::Type::SeekBackward
);
2701 SeekingInProgress
= true;
2706 unsigned howmuch
= Config
.incremental_seeking
2707 ? (Timer
-t
).seconds()/2+Config
.seek_time
2710 Key input
= Key::read(*wFooter
);
2711 auto k
= Bindings
.get(input
);
2712 if (k
.first
== k
.second
|| !k
.first
->isSingle()) // no single action?
2714 auto a
= k
.first
->action();
2715 if (a
== seekForward
)
2717 if (songpos
< Status::State::totalTime())
2718 songpos
= std::min(songpos
+ howmuch
, Status::State::totalTime());
2720 else if (a
== seekBackward
)
2724 if (songpos
< howmuch
)
2733 *wFooter
<< NC::Format::Bold
;
2734 std::string tracklength
;
2735 switch (Config
.design
)
2737 case Design::Classic
:
2739 if (Config
.display_remaining_time
)
2742 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime()-songpos
);
2745 tracklength
+= MPD::Song::ShowTime(songpos
);
2747 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime());
2749 *wFooter
<< NC::XY(wFooter
->getWidth()-tracklength
.length(), 1) << tracklength
;
2751 case Design::Alternative
:
2752 if (Config
.display_remaining_time
)
2755 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime()-songpos
);
2758 tracklength
= MPD::Song::ShowTime(songpos
);
2760 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime());
2761 *wHeader
<< NC::XY(0, 0) << tracklength
<< " ";
2765 *wFooter
<< NC::Format::NoBold
;
2766 Progressbar::draw(songpos
, Status::State::totalTime());
2769 SeekingInProgress
= false;
2770 Mpd
.Seek(Status::State::currentSongPosition(), songpos
);
2772 wFooter
->setTimeout(old_timeout
);
2774 Progressbar::unlock();
2775 Statusbar::unlock();
2778 void findItem(const Find direction
)
2780 using Global::wFooter
;
2782 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
2784 assert(w
->allowsSearching());
2787 Statusbar::put() << "Find " << (direction
== Find::Forward
? "forward" : "backward") << ": ";
2788 std::string findme
= wFooter
->getString();
2789 Statusbar::unlock();
2791 if (!findme
.empty())
2792 Statusbar::print("Searching...");
2794 bool success
= w
->search(findme
);
2800 Statusbar::print("Searching finished");
2802 Statusbar::printf("Unable to find \"%1%\"", findme
);
2804 if (direction
== ::Find::Forward
)
2805 w
->nextFound(Config
.wrapped_search
);
2807 w
->prevFound(Config
.wrapped_search
);
2809 if (myScreen
== myPlaylist
)
2810 myPlaylist
->EnableHighlighting();
2813 void listsChangeFinisher()
2815 if (myScreen
== myLibrary
2816 || myScreen
== myPlaylistEditor
2817 # ifdef HAVE_TAGLIB_H
2818 || myScreen
== myTagEditor
2819 # endif // HAVE_TAGLIB_H
2822 if (myScreen
->activeWindow() == &myLibrary
->Tags
)
2824 myLibrary
->Albums
.clear();
2825 myLibrary
->Albums
.refresh();
2826 myLibrary
->Songs
.clear();
2827 myLibrary
->Songs
.refresh();
2828 myLibrary
->updateTimer();
2830 else if (myScreen
->activeWindow() == &myLibrary
->Albums
)
2832 myLibrary
->Songs
.clear();
2833 myLibrary
->Songs
.refresh();
2834 myLibrary
->updateTimer();
2836 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
))
2838 myPlaylistEditor
->Content
.clear();
2839 myPlaylistEditor
->Content
.refresh();
2840 myPlaylistEditor
->updateTimer();
2842 # ifdef HAVE_TAGLIB_H
2843 else if (myScreen
->activeWindow() == myTagEditor
->Dirs
)
2845 myTagEditor
->Tags
->clear();
2847 # endif // HAVE_TAGLIB_H