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/filesystem/operations.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"
48 #include "menu_impl.h"
52 #include "playlist_editor.h"
53 #include "sort_playlist.h"
54 #include "search_engine.h"
55 #include "sel_items_adder.h"
56 #include "server_info.h"
57 #include "song_info.h"
59 #include "utility/string.h"
60 #include "utility/type_conversions.h"
61 #include "tag_editor.h"
62 #include "tiny_tag_editor.h"
63 #include "visualizer.h"
70 #endif // HAVE_TAGLIB_H
72 using Global::myScreen
;
74 namespace ph
= std::placeholders
;
79 Actions::BaseAction
*, static_cast<size_t>(Actions::Type::_numberOfActions
)
82 void populateActions();
84 bool scrollTagCanBeRun(NC::List
*&list
, SongList
*&songs
);
85 void scrollTagUpRun(NC::List
*list
, SongList
*songs
, MPD::Song::GetFunction get
);
86 void scrollTagDownRun(NC::List
*list
, SongList
*songs
, MPD::Song::GetFunction get
);
89 void findItem(const SearchDirection direction
);
90 void listsChangeFinisher();
92 template <typename Iterator
>
93 bool findSelectedRangeAndPrintInfoIfNot(Iterator
&first
, Iterator
&last
)
95 bool success
= findSelectedRange(first
, last
);
97 Statusbar::print("No range selected");
105 bool OriginalStatusbarVisibility
;
106 bool ExitMainLoop
= false;
112 void validateScreenSize()
114 using Global::MainHeight
;
116 if (COLS
< 30 || MainHeight
< 5)
119 std::cout
<< "Screen is too small to handle ncmpcpp correctly\n";
124 void initializeScreens()
127 myPlaylist
= new Playlist
;
128 myBrowser
= new Browser
;
129 mySearcher
= new SearchEngine
;
130 myLibrary
= new MediaLibrary
;
131 myPlaylistEditor
= new PlaylistEditor
;
132 myLyrics
= new Lyrics
;
133 mySelectedItemsAdder
= new SelectedItemsAdder
;
134 mySongInfo
= new SongInfo
;
135 myServerInfo
= new ServerInfo
;
136 mySortPlaylistDialog
= new SortPlaylistDialog
;
138 # ifdef HAVE_CURL_CURL_H
139 myLastfm
= new Lastfm
;
140 # endif // HAVE_CURL_CURL_H
142 # ifdef HAVE_TAGLIB_H
143 myTinyTagEditor
= new TinyTagEditor
;
144 myTagEditor
= new TagEditor
;
145 # endif // HAVE_TAGLIB_H
147 # ifdef ENABLE_VISUALIZER
148 myVisualizer
= new Visualizer
;
149 # endif // ENABLE_VISUALIZER
151 # ifdef ENABLE_OUTPUTS
152 myOutputs
= new Outputs
;
153 # endif // ENABLE_OUTPUTS
157 # endif // ENABLE_CLOCK
161 void setResizeFlags()
163 myHelp
->hasToBeResized
= 1;
164 myPlaylist
->hasToBeResized
= 1;
165 myBrowser
->hasToBeResized
= 1;
166 mySearcher
->hasToBeResized
= 1;
167 myLibrary
->hasToBeResized
= 1;
168 myPlaylistEditor
->hasToBeResized
= 1;
169 myLyrics
->hasToBeResized
= 1;
170 mySelectedItemsAdder
->hasToBeResized
= 1;
171 mySongInfo
->hasToBeResized
= 1;
172 myServerInfo
->hasToBeResized
= 1;
173 mySortPlaylistDialog
->hasToBeResized
= 1;
175 # ifdef HAVE_CURL_CURL_H
176 myLastfm
->hasToBeResized
= 1;
177 # endif // HAVE_CURL_CURL_H
179 # ifdef HAVE_TAGLIB_H
180 myTinyTagEditor
->hasToBeResized
= 1;
181 myTagEditor
->hasToBeResized
= 1;
182 # endif // HAVE_TAGLIB_H
184 # ifdef ENABLE_VISUALIZER
185 myVisualizer
->hasToBeResized
= 1;
186 # endif // ENABLE_VISUALIZER
188 # ifdef ENABLE_OUTPUTS
189 myOutputs
->hasToBeResized
= 1;
190 # endif // ENABLE_OUTPUTS
193 myClock
->hasToBeResized
= 1;
194 # endif // ENABLE_CLOCK
197 void resizeScreen(bool reload_main_window
)
199 using Global::MainHeight
;
200 using Global::wHeader
;
201 using Global::wFooter
;
203 // update internal screen dimensions
204 if (reload_main_window
)
206 rl_resize_terminal();
211 MainHeight
= LINES
-(Config
.design
== Design::Alternative
? 7 : 4);
213 validateScreenSize();
215 if (!Config
.header_visibility
)
217 if (!Config
.statusbar_visibility
)
222 applyToVisibleWindows(&BaseScreen::resize
);
224 if (Config
.header_visibility
|| Config
.design
== Design::Alternative
)
225 wHeader
->resize(COLS
, HeaderHeight
);
227 FooterStartY
= LINES
-(Config
.statusbar_visibility
? 2 : 1);
228 wFooter
->moveTo(0, FooterStartY
);
229 wFooter
->resize(COLS
, Config
.statusbar_visibility
? 2 : 1);
231 applyToVisibleWindows(&BaseScreen::refresh
);
233 Status::Changes::elapsedTime(false);
234 Status::Changes::playerState();
235 // Note: routines for drawing separator if alternative user
236 // interface is active and header is hidden are placed in
237 // NcmpcppStatusChanges.StatusFlags
238 Status::Changes::flags();
244 void setWindowsDimensions()
246 using Global::MainStartY
;
247 using Global::MainHeight
;
249 MainStartY
= Config
.design
== Design::Alternative
? 5 : 2;
250 MainHeight
= LINES
-(Config
.design
== Design::Alternative
? 7 : 4);
252 if (!Config
.header_visibility
)
257 if (!Config
.statusbar_visibility
)
260 HeaderHeight
= Config
.design
== Design::Alternative
? (Config
.header_visibility
? 5 : 3) : 1;
261 FooterStartY
= LINES
-(Config
.statusbar_visibility
? 2 : 1);
262 FooterHeight
= Config
.statusbar_visibility
? 2 : 1;
265 void confirmAction(const boost::format
&description
)
267 Statusbar::ScopedLock slock
;
268 Statusbar::put() << description
.str()
269 << " [" << NC::Format::Bold
<< 'y' << NC::Format::NoBold
270 << '/' << NC::Format::Bold
<< 'n' << NC::Format::NoBold
272 auto answer
= Statusbar::Helpers::promptReturnOneOf({"y", "n"});
274 throw NC::PromptAborted(std::move(answer
));
277 bool isMPDMusicDirSet()
279 if (Config
.mpd_music_dir
.empty())
281 Statusbar::print("Proper mpd_music_dir variable has to be set in configuration file");
287 BaseAction
&get(Actions::Type at
)
289 if (AvailableActions
[1] == nullptr)
291 BaseAction
*action
= AvailableActions
[static_cast<size_t>(at
)];
292 // action should be always present if action type in queried
293 assert(action
!= nullptr);
297 BaseAction
*get(const std::string
&name
)
299 BaseAction
*result
= 0;
300 if (AvailableActions
[1] == nullptr)
302 for (auto it
= AvailableActions
.begin(); it
!= AvailableActions
.end(); ++it
)
304 if (*it
!= nullptr && (*it
)->name() == name
)
313 UpdateEnvironment::UpdateEnvironment()
314 : BaseAction(Type::UpdateEnvironment
, "update_environment")
315 , m_past(boost::posix_time::from_time_t(0))
318 void UpdateEnvironment::run(bool update_timer
, bool refresh_window
)
322 // update timer, status if necessary etc.
323 Status::trace(update_timer
, true);
326 if ((myScreen
== myPlaylist
|| myScreen
== myBrowser
|| myScreen
== myLyrics
)
327 && (Timer
- m_past
> boost::posix_time::milliseconds(500))
335 myScreen
->refreshWindow();
338 void UpdateEnvironment::run()
343 bool MouseEvent::canBeRun()
345 return Config
.mouse_support
;
348 void MouseEvent::run()
350 using Global::VolumeState
;
351 using Global::wFooter
;
353 m_old_mouse_event
= m_mouse_event
;
354 m_mouse_event
= wFooter
->getMouseEvent();
356 //Statusbar::printf("(%1%, %2%, %3%)", m_mouse_event.bstate, m_mouse_event.x, m_mouse_event.y);
358 if (m_mouse_event
.bstate
& BUTTON1_PRESSED
359 && m_mouse_event
.y
== LINES
-(Config
.statusbar_visibility
? 2 : 1)
362 if (Status::State::player() == MPD::psStop
)
364 Mpd
.Seek(Status::State::currentSongPosition(),
365 Status::State::totalTime()*m_mouse_event
.x
/double(COLS
));
367 else if (m_mouse_event
.bstate
& BUTTON1_PRESSED
368 && (Config
.statusbar_visibility
|| Config
.design
== Design::Alternative
)
369 && Status::State::player() != MPD::psStop
370 && m_mouse_event
.y
== (Config
.design
== Design::Alternative
? 1 : LINES
-1)
371 && m_mouse_event
.x
< 9
376 else if ((m_mouse_event
.bstate
& BUTTON5_PRESSED
|| m_mouse_event
.bstate
& BUTTON4_PRESSED
)
377 && (Config
.header_visibility
|| Config
.design
== Design::Alternative
)
378 && m_mouse_event
.y
== 0 && size_t(m_mouse_event
.x
) > COLS
-VolumeState
.length()
381 if (m_mouse_event
.bstate
& BUTTON5_PRESSED
)
382 get(Type::VolumeDown
).execute();
384 get(Type::VolumeUp
).execute();
386 else if (m_mouse_event
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
| BUTTON4_PRESSED
| BUTTON5_PRESSED
))
387 myScreen
->mouseButtonPressed(m_mouse_event
);
392 myScreen
->scroll(NC::Scroll::Up
);
393 listsChangeFinisher();
396 void ScrollDown::run()
398 myScreen
->scroll(NC::Scroll::Down
);
399 listsChangeFinisher();
402 bool ScrollUpArtist::canBeRun()
404 return scrollTagCanBeRun(m_list
, m_songs
);
407 void ScrollUpArtist::run()
409 scrollTagUpRun(m_list
, m_songs
, &MPD::Song::getArtist
);
412 bool ScrollUpAlbum::canBeRun()
414 return scrollTagCanBeRun(m_list
, m_songs
);
417 void ScrollUpAlbum::run()
419 scrollTagUpRun(m_list
, m_songs
, &MPD::Song::getAlbum
);
422 bool ScrollDownArtist::canBeRun()
424 return scrollTagCanBeRun(m_list
, m_songs
);
427 void ScrollDownArtist::run()
429 scrollTagDownRun(m_list
, m_songs
, &MPD::Song::getArtist
);
432 bool ScrollDownAlbum::canBeRun()
434 return scrollTagCanBeRun(m_list
, m_songs
);
437 void ScrollDownAlbum::run()
439 scrollTagDownRun(m_list
, m_songs
, &MPD::Song::getAlbum
);
444 myScreen
->scroll(NC::Scroll::PageUp
);
445 listsChangeFinisher();
450 myScreen
->scroll(NC::Scroll::PageDown
);
451 listsChangeFinisher();
456 myScreen
->scroll(NC::Scroll::Home
);
457 listsChangeFinisher();
462 myScreen
->scroll(NC::Scroll::End
);
463 listsChangeFinisher();
466 void ToggleInterface::run()
468 switch (Config
.design
)
470 case Design::Classic
:
471 Config
.design
= Design::Alternative
;
472 Config
.statusbar_visibility
= false;
474 case Design::Alternative
:
475 Config
.design
= Design::Classic
;
476 Config
.statusbar_visibility
= OriginalStatusbarVisibility
;
479 setWindowsDimensions();
481 // unlock progressbar
482 Progressbar::ScopedLock();
483 Status::Changes::mixer();
484 Status::Changes::elapsedTime(false);
485 Statusbar::printf("User interface: %1%", Config
.design
);
488 bool JumpToParentDirectory::canBeRun()
490 return (myScreen
== myBrowser
)
491 # ifdef HAVE_TAGLIB_H
492 || (myScreen
->activeWindow() == myTagEditor
->Dirs
)
493 # endif // HAVE_TAGLIB_H
497 void JumpToParentDirectory::run()
499 if (myScreen
== myBrowser
)
501 if (!myBrowser
->inRootDirectory())
503 myBrowser
->main().reset();
504 myBrowser
->enterPressed();
507 # ifdef HAVE_TAGLIB_H
508 else if (myScreen
== myTagEditor
)
510 if (myTagEditor
->CurrentDir() != "/")
512 myTagEditor
->Dirs
->reset();
513 myTagEditor
->enterPressed();
516 # endif // HAVE_TAGLIB_H
519 void PressEnter::run()
521 myScreen
->enterPressed();
524 bool PreviousColumn::canBeRun()
526 auto hc
= hasColumns(myScreen
);
527 return hc
&& hc
->previousColumnAvailable();
530 void PreviousColumn::run()
532 hasColumns(myScreen
)->previousColumn();
535 bool NextColumn::canBeRun()
537 auto hc
= hasColumns(myScreen
);
538 return hc
&& hc
->nextColumnAvailable();
541 void NextColumn::run()
543 hasColumns(myScreen
)->nextColumn();
546 bool MasterScreen::canBeRun()
548 using Global::myLockedScreen
;
549 using Global::myInactiveScreen
;
551 return myLockedScreen
553 && myLockedScreen
!= myScreen
554 && myScreen
->isMergable();
557 void MasterScreen::run()
559 using Global::myInactiveScreen
;
560 using Global::myLockedScreen
;
562 myInactiveScreen
= myScreen
;
563 myScreen
= myLockedScreen
;
567 bool SlaveScreen::canBeRun()
569 using Global::myLockedScreen
;
570 using Global::myInactiveScreen
;
572 return myLockedScreen
574 && myLockedScreen
== myScreen
575 && myScreen
->isMergable();
578 void SlaveScreen::run()
580 using Global::myInactiveScreen
;
581 using Global::myLockedScreen
;
583 myScreen
= myInactiveScreen
;
584 myInactiveScreen
= myLockedScreen
;
590 int volume
= std::min(Status::State::volume()+Config
.volume_change_step
, 100u);
591 Mpd
.SetVolume(volume
);
594 void VolumeDown::run()
596 int volume
= std::max(int(Status::State::volume()-Config
.volume_change_step
), 0);
597 Mpd
.SetVolume(volume
);
600 bool AddItemToPlaylist::canBeRun()
602 if (m_hs
!= static_cast<void *>(myScreen
))
603 m_hs
= dynamic_cast<HasSongs
*>(myScreen
);
604 return m_hs
!= nullptr;
607 void AddItemToPlaylist::run()
609 bool success
= m_hs
->addItemToPlaylist();
612 myScreen
->scroll(NC::Scroll::Down
);
613 listsChangeFinisher();
617 bool DeletePlaylistItems::canBeRun()
619 return (myScreen
== myPlaylist
&& !myPlaylist
->main().empty())
620 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
) && !myPlaylistEditor
->Content
.empty());
623 void DeletePlaylistItems::run()
625 if (myScreen
== myPlaylist
)
627 Statusbar::print("Deleting items...");
628 auto delete_fun
= std::bind(&MPD::Connection::Delete
, ph::_1
, ph::_2
);
629 deleteSelectedSongs(myPlaylist
->main(), delete_fun
);
630 Statusbar::print("Item(s) deleted");
632 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Content
))
634 std::string playlist
= myPlaylistEditor
->Playlists
.current()->value().path();
635 auto delete_fun
= std::bind(&MPD::Connection::PlaylistDelete
, ph::_1
, playlist
, ph::_2
);
636 Statusbar::print("Deleting items...");
637 deleteSelectedSongs(myPlaylistEditor
->Content
, delete_fun
);
638 Statusbar::print("Item(s) deleted");
642 bool DeleteBrowserItems::canBeRun()
644 auto check_if_deletion_allowed
= []() {
645 if (Config
.allow_for_physical_item_deletion
)
649 Statusbar::print("Flag \"allow_for_physical_item_deletion\" needs to be enabled in configuration file");
653 return myScreen
== myBrowser
654 && !myBrowser
->main().empty()
655 && isMPDMusicDirSet()
656 && check_if_deletion_allowed();
659 void DeleteBrowserItems::run()
661 auto get_name
= [](const MPD::Item
&item
) -> std::string
{
665 case MPD::Item::Type::Directory
:
666 iname
= getBasename(item
.directory().path());
668 case MPD::Item::Type::Song
:
669 iname
= item
.song().getName();
671 case MPD::Item::Type::Playlist
:
672 iname
= getBasename(item
.playlist().path());
678 boost::format question
;
679 if (hasSelected(myBrowser
->main().begin(), myBrowser
->main().end()))
680 question
= boost::format("Delete selected items?");
683 const auto &item
= myBrowser
->main().current()->value();
684 // parent directories are not accepted (and they
685 // can't be selected, so in other cases it's fine).
686 if (myBrowser
->isParentDirectory(item
))
688 const char msg
[] = "Delete \"%1%\"?";
689 question
= boost::format(msg
) % wideShorten(
690 get_name(item
), COLS
-const_strlen(msg
)-5
693 confirmAction(question
);
695 auto items
= getSelectedOrCurrent(
696 myBrowser
->main().begin(),
697 myBrowser
->main().end(),
698 myBrowser
->main().current()
700 for (const auto &item
: items
)
702 myBrowser
->remove(item
->value());
703 const char msg
[] = "Deleted %1% \"%2%\"";
704 Statusbar::printf(msg
,
705 itemTypeToString(item
->value().type()),
706 wideShorten(get_name(item
->value()), COLS
-const_strlen(msg
))
710 if (!myBrowser
->isLocal())
711 Mpd
.UpdateDirectory(myBrowser
->currentDirectory());
712 myBrowser
->requestUpdate();
715 bool DeleteStoredPlaylist::canBeRun()
717 return myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
);
720 void DeleteStoredPlaylist::run()
722 if (myPlaylistEditor
->Playlists
.empty())
724 boost::format question
;
725 if (hasSelected(myPlaylistEditor
->Playlists
.begin(), myPlaylistEditor
->Playlists
.end()))
726 question
= boost::format("Delete selected playlists?");
728 question
= boost::format("Delete playlist \"%1%\"?")
729 % wideShorten(myPlaylistEditor
->Playlists
.current()->value().path(), COLS
-question
.size()-10);
730 confirmAction(question
);
731 auto list
= getSelectedOrCurrent(
732 myPlaylistEditor
->Playlists
.begin(),
733 myPlaylistEditor
->Playlists
.end(),
734 myPlaylistEditor
->Playlists
.current()
736 for (const auto &item
: list
)
737 Mpd
.DeletePlaylist(item
->value().path());
738 Statusbar::printf("%1% deleted", list
.size() == 1 ? "Playlist" : "Playlists");
739 // force playlists update. this happens automatically, but only after call
740 // to Key::read, therefore when we call PlaylistEditor::Update, it won't
741 // yet see it, so let's point that it needs to update it.
742 myPlaylistEditor
->requestPlaylistsUpdate();
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
;
770 std::string playlist_name
;
772 Statusbar::ScopedLock slock
;
773 Statusbar::put() << "Save playlist as: ";
774 playlist_name
= wFooter
->prompt();
778 Mpd
.SavePlaylist(playlist_name
);
779 Statusbar::printf("Playlist saved as \"%1%\"", playlist_name
);
781 catch (MPD::ServerError
&e
)
783 if (e
.code() == MPD_SERVER_ERROR_EXIST
)
786 boost::format("Playlist \"%1%\" already exists, overwrite?") % playlist_name
788 Mpd
.DeletePlaylist(playlist_name
);
789 Mpd
.SavePlaylist(playlist_name
);
790 Statusbar::print("Playlist overwritten");
802 void ExecuteCommand::run()
804 using Global::wFooter
;
806 std::string cmd_name
;
808 Statusbar::ScopedLock slock
;
809 NC::Window::ScopedPromptHook
helper(*wFooter
,
810 Statusbar::Helpers::TryExecuteImmediateCommand()
812 Statusbar::put() << NC::Format::Bold
<< ":" << NC::Format::NoBold
;
813 cmd_name
= wFooter
->prompt();
816 auto cmd
= Bindings
.findCommand(cmd_name
);
819 Statusbar::printf(1, "Executing %1%...", cmd_name
);
820 bool res
= cmd
->binding().execute();
821 Statusbar::printf("Execution of command \"%1%\" %2%.",
822 cmd_name
, res
? "successful" : "unsuccessful"
826 Statusbar::printf("No command named \"%1%\"", cmd_name
);
829 bool MoveSortOrderUp::canBeRun()
831 return myScreen
== mySortPlaylistDialog
;
834 void MoveSortOrderUp::run()
836 mySortPlaylistDialog
->moveSortOrderUp();
839 bool MoveSortOrderDown::canBeRun()
841 return myScreen
== mySortPlaylistDialog
;
844 void MoveSortOrderDown::run()
846 mySortPlaylistDialog
->moveSortOrderDown();
849 bool MoveSelectedItemsUp::canBeRun()
851 return ((myScreen
== myPlaylist
852 && !myPlaylist
->main().empty())
853 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
)
854 && !myPlaylistEditor
->Content
.empty()));
857 void MoveSelectedItemsUp::run()
859 if (myScreen
== myPlaylist
)
861 moveSelectedItemsUp(myPlaylist
->main(), std::bind(&MPD::Connection::Move
, ph::_1
, ph::_2
, ph::_3
));
863 else if (myScreen
== myPlaylistEditor
)
865 assert(!myPlaylistEditor
->Playlists
.empty());
866 std::string playlist
= myPlaylistEditor
->Playlists
.current()->value().path();
867 auto move_fun
= std::bind(&MPD::Connection::PlaylistMove
, ph::_1
, playlist
, ph::_2
, ph::_3
);
868 moveSelectedItemsUp(myPlaylistEditor
->Content
, move_fun
);
872 bool MoveSelectedItemsDown::canBeRun()
874 return ((myScreen
== myPlaylist
875 && !myPlaylist
->main().empty())
876 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
)
877 && !myPlaylistEditor
->Content
.empty()));
880 void MoveSelectedItemsDown::run()
882 if (myScreen
== myPlaylist
)
884 moveSelectedItemsDown(myPlaylist
->main(), std::bind(&MPD::Connection::Move
, ph::_1
, ph::_2
, ph::_3
));
886 else if (myScreen
== myPlaylistEditor
)
888 assert(!myPlaylistEditor
->Playlists
.empty());
889 std::string playlist
= myPlaylistEditor
->Playlists
.current()->value().path();
890 auto move_fun
= std::bind(&MPD::Connection::PlaylistMove
, ph::_1
, playlist
, ph::_2
, ph::_3
);
891 moveSelectedItemsDown(myPlaylistEditor
->Content
, move_fun
);
895 bool MoveSelectedItemsTo::canBeRun()
897 return myScreen
== myPlaylist
898 || myScreen
->isActiveWindow(myPlaylistEditor
->Content
);
901 void MoveSelectedItemsTo::run()
903 if (myScreen
== myPlaylist
)
905 if (!myPlaylist
->main().empty())
906 moveSelectedItemsTo(myPlaylist
->main(), std::bind(&MPD::Connection::Move
, ph::_1
, ph::_2
, ph::_3
));
910 assert(!myPlaylistEditor
->Playlists
.empty());
911 std::string playlist
= myPlaylistEditor
->Playlists
.current()->value().path();
912 auto move_fun
= std::bind(&MPD::Connection::PlaylistMove
, ph::_1
, playlist
, ph::_2
, ph::_3
);
913 moveSelectedItemsTo(myPlaylistEditor
->Content
, move_fun
);
919 return myScreen
!= myPlaylistEditor
920 || !myPlaylistEditor
->Playlists
.empty();
925 using Global::wFooter
;
929 Statusbar::ScopedLock slock
;
930 Statusbar::put() << (myScreen
== myPlaylistEditor
? "Add to playlist: " : "Add: ");
931 path
= wFooter
->prompt();
934 // confirm when one wants to add the whole database
936 confirmAction("Are you sure you want to add the whole database?");
938 Statusbar::put() << "Adding...";
940 if (myScreen
== myPlaylistEditor
)
941 Mpd
.AddToPlaylist(myPlaylistEditor
->Playlists
.current()->value().path(), path
);
948 catch (MPD::ServerError
&err
)
950 // If a path is not a file or directory, assume it is a playlist.
951 if (err
.code() == MPD_SERVER_ERROR_NO_EXIST
)
952 Mpd
.LoadPlaylist(path
);
959 bool SeekForward::canBeRun()
961 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
964 void SeekForward::run()
969 bool SeekBackward::canBeRun()
971 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
974 void SeekBackward::run()
979 bool ToggleDisplayMode::canBeRun()
981 return myScreen
== myPlaylist
982 || myScreen
== myBrowser
983 || myScreen
== mySearcher
984 || myScreen
->isActiveWindow(myPlaylistEditor
->Content
);
987 void ToggleDisplayMode::run()
989 if (myScreen
== myPlaylist
)
991 switch (Config
.playlist_display_mode
)
993 case DisplayMode::Classic
:
994 Config
.playlist_display_mode
= DisplayMode::Columns
;
995 myPlaylist
->main().setItemDisplayer(std::bind(
996 Display::SongsInColumns
, ph::_1
, std::cref(myPlaylist
->main())
998 if (Config
.titles_visibility
)
999 myPlaylist
->main().setTitle(Display::Columns(myPlaylist
->main().getWidth()));
1001 myPlaylist
->main().setTitle("");
1003 case DisplayMode::Columns
:
1004 Config
.playlist_display_mode
= DisplayMode::Classic
;
1005 myPlaylist
->main().setItemDisplayer(std::bind(
1006 Display::Songs
, ph::_1
, std::cref(myPlaylist
->main()), std::cref(Config
.song_list_format
)
1008 myPlaylist
->main().setTitle("");
1010 Statusbar::printf("Playlist display mode: %1%", Config
.playlist_display_mode
);
1012 else if (myScreen
== myBrowser
)
1014 switch (Config
.browser_display_mode
)
1016 case DisplayMode::Classic
:
1017 Config
.browser_display_mode
= DisplayMode::Columns
;
1018 if (Config
.titles_visibility
)
1019 myBrowser
->main().setTitle(Display::Columns(myBrowser
->main().getWidth()));
1021 myBrowser
->main().setTitle("");
1023 case DisplayMode::Columns
:
1024 Config
.browser_display_mode
= DisplayMode::Classic
;
1025 myBrowser
->main().setTitle("");
1028 Statusbar::printf("Browser display mode: %1%", Config
.browser_display_mode
);
1030 else if (myScreen
== mySearcher
)
1032 switch (Config
.search_engine_display_mode
)
1034 case DisplayMode::Classic
:
1035 Config
.search_engine_display_mode
= DisplayMode::Columns
;
1037 case DisplayMode::Columns
:
1038 Config
.search_engine_display_mode
= DisplayMode::Classic
;
1041 Statusbar::printf("Search engine display mode: %1%", Config
.search_engine_display_mode
);
1042 if (mySearcher
->main().size() > SearchEngine::StaticOptions
)
1043 mySearcher
->main().setTitle(
1044 Config
.search_engine_display_mode
== DisplayMode::Columns
1045 && Config
.titles_visibility
1046 ? Display::Columns(mySearcher
->main().getWidth())
1050 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Content
))
1052 switch (Config
.playlist_editor_display_mode
)
1054 case DisplayMode::Classic
:
1055 Config
.playlist_editor_display_mode
= DisplayMode::Columns
;
1056 myPlaylistEditor
->Content
.setItemDisplayer(std::bind(
1057 Display::SongsInColumns
, ph::_1
, std::cref(myPlaylistEditor
->Content
)
1060 case DisplayMode::Columns
:
1061 Config
.playlist_editor_display_mode
= DisplayMode::Classic
;
1062 myPlaylistEditor
->Content
.setItemDisplayer(std::bind(
1063 Display::Songs
, ph::_1
, std::cref(myPlaylistEditor
->Content
), std::cref(Config
.song_list_format
)
1067 Statusbar::printf("Playlist editor display mode: %1%", Config
.playlist_editor_display_mode
);
1071 bool ToggleSeparatorsBetweenAlbums::canBeRun()
1076 void ToggleSeparatorsBetweenAlbums::run()
1078 Config
.playlist_separate_albums
= !Config
.playlist_separate_albums
;
1079 Statusbar::printf("Separators between albums: %1%",
1080 Config
.playlist_separate_albums
? "on" : "off"
1084 bool ToggleLyricsUpdateOnSongChange::canBeRun()
1086 return myScreen
== myLyrics
;
1089 void ToggleLyricsUpdateOnSongChange::run()
1091 Config
.now_playing_lyrics
= !Config
.now_playing_lyrics
;
1092 Statusbar::printf("Update lyrics if song changes: %1%",
1093 Config
.now_playing_lyrics
? "on" : "off"
1097 #ifndef HAVE_CURL_CURL_H
1098 bool ToggleLyricsFetcher::canBeRun()
1102 #endif // NOT HAVE_CURL_CURL_H
1104 void ToggleLyricsFetcher::run()
1106 # ifdef HAVE_CURL_CURL_H
1107 myLyrics
->ToggleFetcher();
1108 # endif // HAVE_CURL_CURL_H
1111 #ifndef HAVE_CURL_CURL_H
1112 bool ToggleFetchingLyricsInBackground::canBeRun()
1116 #endif // NOT HAVE_CURL_CURL_H
1118 void ToggleFetchingLyricsInBackground::run()
1120 # ifdef HAVE_CURL_CURL_H
1121 Config
.fetch_lyrics_in_background
= !Config
.fetch_lyrics_in_background
;
1122 Statusbar::printf("Fetching lyrics for playing songs in background: %1%",
1123 Config
.fetch_lyrics_in_background
? "on" : "off"
1125 # endif // HAVE_CURL_CURL_H
1128 void TogglePlayingSongCentering::run()
1130 Config
.autocenter_mode
= !Config
.autocenter_mode
;
1131 Statusbar::printf("Centering playing song: %1%",
1132 Config
.autocenter_mode
? "on" : "off"
1134 if (Config
.autocenter_mode
)
1136 auto s
= myPlaylist
->nowPlayingSong();
1138 myPlaylist
->main().highlight(s
.getPosition());
1142 void UpdateDatabase::run()
1144 if (myScreen
== myBrowser
)
1145 Mpd
.UpdateDirectory(myBrowser
->currentDirectory());
1146 # ifdef HAVE_TAGLIB_H
1147 else if (myScreen
== myTagEditor
)
1148 Mpd
.UpdateDirectory(myTagEditor
->CurrentDir());
1149 # endif // HAVE_TAGLIB_H
1151 Mpd
.UpdateDirectory("/");
1154 bool JumpToPlayingSong::canBeRun()
1156 return myScreen
== myPlaylist
1157 || myScreen
== myBrowser
1158 || myScreen
== myLibrary
;
1161 void JumpToPlayingSong::run()
1163 auto s
= myPlaylist
->nowPlayingSong();
1166 if (myScreen
== myPlaylist
)
1168 myPlaylist
->main().highlight(s
.getPosition());
1170 else if (myScreen
== myBrowser
)
1172 myBrowser
->locateSong(s
);
1174 else if (myScreen
== myLibrary
)
1176 myLibrary
->LocateSong(s
);
1180 void ToggleRepeat::run()
1182 Mpd
.SetRepeat(!Status::State::repeat());
1185 bool Shuffle::canBeRun()
1187 if (myScreen
!= myPlaylist
)
1189 m_begin
= myPlaylist
->main().begin();
1190 m_end
= myPlaylist
->main().end();
1191 return findSelectedRangeAndPrintInfoIfNot(m_begin
, m_end
);
1196 auto begin
= myPlaylist
->main().begin();
1197 Mpd
.ShuffleRange(m_begin
-begin
, m_end
-begin
);
1198 Statusbar::print("Range shuffled");
1201 void ToggleRandom::run()
1203 Mpd
.SetRandom(!Status::State::random());
1206 bool StartSearching::canBeRun()
1208 return myScreen
== mySearcher
&& !mySearcher
->main()[0].isInactive();
1211 void StartSearching::run()
1213 mySearcher
->main().highlight(SearchEngine::SearchButton
);
1214 mySearcher
->main().setHighlighting(0);
1215 mySearcher
->main().refresh();
1216 mySearcher
->main().setHighlighting(1);
1217 mySearcher
->enterPressed();
1220 bool SaveTagChanges::canBeRun()
1222 # ifdef HAVE_TAGLIB_H
1223 return myScreen
== myTinyTagEditor
1224 || myScreen
->activeWindow() == myTagEditor
->TagTypes
;
1227 # endif // HAVE_TAGLIB_H
1230 void SaveTagChanges::run()
1232 # ifdef HAVE_TAGLIB_H
1233 if (myScreen
== myTinyTagEditor
)
1235 myTinyTagEditor
->main().highlight(myTinyTagEditor
->main().size()-2); // Save
1236 myTinyTagEditor
->enterPressed();
1238 else if (myScreen
->activeWindow() == myTagEditor
->TagTypes
)
1240 myTagEditor
->TagTypes
->highlight(myTagEditor
->TagTypes
->size()-1); // Save
1241 myTagEditor
->enterPressed();
1243 # endif // HAVE_TAGLIB_H
1246 void ToggleSingle::run()
1248 Mpd
.SetSingle(!Status::State::single());
1251 void ToggleConsume::run()
1253 Mpd
.SetConsume(!Status::State::consume());
1256 void ToggleCrossfade::run()
1258 Mpd
.SetCrossfade(Status::State::crossfade() ? 0 : Config
.crossfade_time
);
1261 void SetCrossfade::run()
1263 using Global::wFooter
;
1265 Statusbar::ScopedLock slock
;
1266 Statusbar::put() << "Set crossfade to: ";
1267 auto crossfade
= fromString
<unsigned>(wFooter
->prompt());
1268 lowerBoundCheck(crossfade
, 0u);
1269 Config
.crossfade_time
= crossfade
;
1270 Mpd
.SetCrossfade(crossfade
);
1273 void SetVolume::run()
1275 using Global::wFooter
;
1279 Statusbar::ScopedLock slock
;
1280 Statusbar::put() << "Set volume to: ";
1281 volume
= fromString
<unsigned>(wFooter
->prompt());
1282 boundsCheck(volume
, 0u, 100u);
1283 Mpd
.SetVolume(volume
);
1285 Statusbar::printf("Volume set to %1%%%", volume
);
1288 bool EditSong::canBeRun()
1290 # ifdef HAVE_TAGLIB_H
1291 m_song
= currentSong(myScreen
);
1292 return m_song
!= nullptr && isMPDMusicDirSet();
1295 # endif // HAVE_TAGLIB_H
1298 void EditSong::run()
1300 # ifdef HAVE_TAGLIB_H
1301 myTinyTagEditor
->SetEdited(*m_song
);
1302 myTinyTagEditor
->switchTo();
1303 # endif // HAVE_TAGLIB_H
1306 bool EditLibraryTag::canBeRun()
1308 # ifdef HAVE_TAGLIB_H
1309 return myScreen
->isActiveWindow(myLibrary
->Tags
)
1310 && !myLibrary
->Tags
.empty()
1311 && isMPDMusicDirSet();
1314 # endif // HAVE_TAGLIB_H
1317 void EditLibraryTag::run()
1319 # ifdef HAVE_TAGLIB_H
1320 using Global::wFooter
;
1322 std::string new_tag
;
1324 Statusbar::ScopedLock slock
;
1325 Statusbar::put() << NC::Format::Bold
<< tagTypeToString(Config
.media_lib_primary_tag
) << NC::Format::NoBold
<< ": ";
1326 new_tag
= wFooter
->prompt(myLibrary
->Tags
.current()->value().tag());
1328 if (!new_tag
.empty() && new_tag
!= myLibrary
->Tags
.current()->value().tag())
1330 Statusbar::print("Updating tags...");
1331 Mpd
.StartSearch(true);
1332 Mpd
.AddSearch(Config
.media_lib_primary_tag
, myLibrary
->Tags
.current()->value().tag());
1333 MPD::MutableSong::SetFunction set
= tagTypeToSetFunction(Config
.media_lib_primary_tag
);
1335 bool success
= true;
1336 std::string dir_to_update
;
1337 for (MPD::SongIterator s
= Mpd
.CommitSearchSongs(), end
; s
!= end
; ++s
)
1339 MPD::MutableSong ms
= std::move(*s
);
1340 ms
.setTags(set
, new_tag
);
1341 Statusbar::printf("Updating tags in \"%1%\"...", ms
.getName());
1342 std::string path
= Config
.mpd_music_dir
+ ms
.getURI();
1343 if (!Tags::write(ms
))
1346 const char msg
[] = "Error while updating tags in \"%1%\"";
1347 Statusbar::printf(msg
, wideShorten(ms
.getURI(), COLS
-const_strlen(msg
)));
1351 if (dir_to_update
.empty())
1352 dir_to_update
= ms
.getURI();
1354 dir_to_update
= getSharedDirectory(dir_to_update
, ms
.getURI());
1358 Mpd
.UpdateDirectory(dir_to_update
);
1359 Statusbar::print("Tags updated successfully");
1362 # endif // HAVE_TAGLIB_H
1365 bool EditLibraryAlbum::canBeRun()
1367 # ifdef HAVE_TAGLIB_H
1368 return myScreen
->isActiveWindow(myLibrary
->Albums
)
1369 && !myLibrary
->Albums
.empty()
1370 && isMPDMusicDirSet();
1373 # endif // HAVE_TAGLIB_H
1376 void EditLibraryAlbum::run()
1378 # ifdef HAVE_TAGLIB_H
1379 using Global::wFooter
;
1380 // FIXME: merge this and EditLibraryTag. also, prompt on failure if user wants to continue
1381 std::string new_album
;
1383 Statusbar::ScopedLock slock
;
1384 Statusbar::put() << NC::Format::Bold
<< "Album: " << NC::Format::NoBold
;
1385 new_album
= wFooter
->prompt(myLibrary
->Albums
.current()->value().entry().album());
1387 if (!new_album
.empty() && new_album
!= myLibrary
->Albums
.current()->value().entry().album())
1390 Statusbar::print("Updating tags...");
1391 for (size_t i
= 0; i
< myLibrary
->Songs
.size(); ++i
)
1393 Statusbar::printf("Updating tags in \"%1%\"...", myLibrary
->Songs
[i
].value().getName());
1394 std::string path
= Config
.mpd_music_dir
+ myLibrary
->Songs
[i
].value().getURI();
1395 TagLib::FileRef
f(path
.c_str());
1398 const char msg
[] = "Error while opening file \"%1%\"";
1399 Statusbar::printf(msg
, wideShorten(myLibrary
->Songs
[i
].value().getURI(), COLS
-const_strlen(msg
)));
1403 f
.tag()->setAlbum(ToWString(new_album
));
1406 const char msg
[] = "Error while writing tags in \"%1%\"";
1407 Statusbar::printf(msg
, wideShorten(myLibrary
->Songs
[i
].value().getURI(), COLS
-const_strlen(msg
)));
1414 Mpd
.UpdateDirectory(getSharedDirectory(myLibrary
->Songs
.beginV(), myLibrary
->Songs
.endV()));
1415 Statusbar::print("Tags updated successfully");
1418 # endif // HAVE_TAGLIB_H
1421 bool EditDirectoryName::canBeRun()
1423 return ((myScreen
== myBrowser
1424 && !myBrowser
->main().empty()
1425 && myBrowser
->main().current()->value().type() == MPD::Item::Type::Directory
)
1426 # ifdef HAVE_TAGLIB_H
1427 || (myScreen
->activeWindow() == myTagEditor
->Dirs
1428 && !myTagEditor
->Dirs
->empty()
1429 && myTagEditor
->Dirs
->choice() > 0)
1430 # endif // HAVE_TAGLIB_H
1431 ) && isMPDMusicDirSet();
1434 void EditDirectoryName::run()
1436 using Global::wFooter
;
1437 if (myScreen
== myBrowser
)
1439 std::string old_dir
= myBrowser
->main().current()->value().directory().path();
1440 std::string new_dir
;
1442 Statusbar::ScopedLock slock
;
1443 Statusbar::put() << NC::Format::Bold
<< "Directory: " << NC::Format::NoBold
;
1444 new_dir
= wFooter
->prompt(old_dir
);
1446 if (!new_dir
.empty() && new_dir
!= old_dir
)
1448 std::string full_old_dir
;
1449 if (!myBrowser
->isLocal())
1450 full_old_dir
+= Config
.mpd_music_dir
;
1451 full_old_dir
+= old_dir
;
1452 std::string full_new_dir
;
1453 if (!myBrowser
->isLocal())
1454 full_new_dir
+= Config
.mpd_music_dir
;
1455 full_new_dir
+= new_dir
;
1456 boost::filesystem::rename(full_old_dir
, full_new_dir
);
1457 const char msg
[] = "Directory renamed to \"%1%\"";
1458 Statusbar::printf(msg
, wideShorten(new_dir
, COLS
-const_strlen(msg
)));
1459 if (!myBrowser
->isLocal())
1460 Mpd
.UpdateDirectory(getSharedDirectory(old_dir
, new_dir
));
1461 myBrowser
->requestUpdate();
1464 # ifdef HAVE_TAGLIB_H
1465 else if (myScreen
->activeWindow() == myTagEditor
->Dirs
)
1467 std::string old_dir
= myTagEditor
->Dirs
->current()->value().first
, new_dir
;
1469 Statusbar::ScopedLock slock
;
1470 Statusbar::put() << NC::Format::Bold
<< "Directory: " << NC::Format::NoBold
;
1471 new_dir
= wFooter
->prompt(old_dir
);
1473 if (!new_dir
.empty() && new_dir
!= old_dir
)
1475 std::string full_old_dir
= Config
.mpd_music_dir
+ myTagEditor
->CurrentDir() + "/" + old_dir
;
1476 std::string full_new_dir
= Config
.mpd_music_dir
+ myTagEditor
->CurrentDir() + "/" + new_dir
;
1477 if (rename(full_old_dir
.c_str(), full_new_dir
.c_str()) == 0)
1479 const char msg
[] = "Directory renamed to \"%1%\"";
1480 Statusbar::printf(msg
, wideShorten(new_dir
, COLS
-const_strlen(msg
)));
1481 Mpd
.UpdateDirectory(myTagEditor
->CurrentDir());
1485 const char msg
[] = "Couldn't rename \"%1%\": %2%";
1486 Statusbar::printf(msg
, wideShorten(old_dir
, COLS
-const_strlen(msg
)-25), strerror(errno
));
1490 # endif // HAVE_TAGLIB_H
1493 bool EditPlaylistName::canBeRun()
1495 return (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
)
1496 && !myPlaylistEditor
->Playlists
.empty())
1497 || (myScreen
== myBrowser
1498 && !myBrowser
->main().empty()
1499 && myBrowser
->main().current()->value().type() == MPD::Item::Type::Playlist
);
1502 void EditPlaylistName::run()
1504 using Global::wFooter
;
1505 std::string old_name
, new_name
;
1506 if (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
))
1507 old_name
= myPlaylistEditor
->Playlists
.current()->value().path();
1509 old_name
= myBrowser
->main().current()->value().playlist().path();
1511 Statusbar::ScopedLock slock
;
1512 Statusbar::put() << NC::Format::Bold
<< "Playlist: " << NC::Format::NoBold
;
1513 new_name
= wFooter
->prompt(old_name
);
1515 if (!new_name
.empty() && new_name
!= old_name
)
1517 Mpd
.Rename(old_name
, new_name
);
1518 const char msg
[] = "Playlist renamed to \"%1%\"";
1519 Statusbar::printf(msg
, wideShorten(new_name
, COLS
-const_strlen(msg
)));
1523 bool EditLyrics::canBeRun()
1525 return myScreen
== myLyrics
;
1528 void EditLyrics::run()
1533 bool JumpToBrowser::canBeRun()
1535 m_song
= currentSong(myScreen
);
1536 return m_song
!= nullptr;
1539 void JumpToBrowser::run()
1541 myBrowser
->locateSong(*m_song
);
1544 bool JumpToMediaLibrary::canBeRun()
1546 m_song
= currentSong(myScreen
);
1547 return m_song
!= nullptr;
1550 void JumpToMediaLibrary::run()
1552 myLibrary
->LocateSong(*m_song
);
1555 bool JumpToPlaylistEditor::canBeRun()
1557 return myScreen
== myBrowser
1558 && myBrowser
->main().current()->value().type() == MPD::Item::Type::Playlist
;
1561 void JumpToPlaylistEditor::run()
1563 myPlaylistEditor
->Locate(myBrowser
->main().current()->value().playlist());
1566 void ToggleScreenLock::run()
1568 using Global::wFooter
;
1569 using Global::myLockedScreen
;
1570 const char *msg_unlockable_screen
= "Current screen can't be locked";
1571 if (myLockedScreen
!= nullptr)
1573 BaseScreen::unlock();
1574 Actions::setResizeFlags();
1576 Statusbar::print("Screen unlocked");
1578 else if (!myScreen
->isLockable())
1580 Statusbar::print(msg_unlockable_screen
);
1584 unsigned part
= Config
.locked_screen_width_part
*100;
1585 if (Config
.ask_for_locked_screen_width_part
)
1587 Statusbar::ScopedLock slock
;
1588 Statusbar::put() << "% of the locked screen's width to be reserved (20-80): ";
1589 part
= fromString
<unsigned>(wFooter
->prompt(boost::lexical_cast
<std::string
>(part
)));
1591 boundsCheck(part
, 20u, 80u);
1592 Config
.locked_screen_width_part
= part
/100.0;
1593 if (myScreen
->lock())
1594 Statusbar::printf("Screen locked (with %1%%% width)", part
);
1596 Statusbar::print(msg_unlockable_screen
);
1600 bool JumpToTagEditor::canBeRun()
1602 # ifdef HAVE_TAGLIB_H
1603 m_song
= currentSong(myScreen
);
1604 return m_song
!= nullptr && isMPDMusicDirSet();
1607 # endif // HAVE_TAGLIB_H
1610 void JumpToTagEditor::run()
1612 # ifdef HAVE_TAGLIB_H
1613 myTagEditor
->LocateSong(*m_song
);
1614 # endif // HAVE_TAGLIB_H
1617 bool JumpToPositionInSong::canBeRun()
1619 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
1622 void JumpToPositionInSong::run()
1624 using Global::wFooter
;
1626 const MPD::Song s
= myPlaylist
->nowPlayingSong();
1630 Statusbar::ScopedLock slock
;
1631 Statusbar::put() << "Position to go (in %/m:ss/seconds(s)): ";
1632 spos
= wFooter
->prompt();
1637 if (boost::regex_match(spos
, what
, rx
.assign("([0-9]+):([0-9]{2})"))) // mm:ss
1639 auto mins
= fromString
<unsigned>(what
[1]);
1640 auto secs
= fromString
<unsigned>(what
[2]);
1641 boundsCheck(secs
, 0u, 60u);
1642 Mpd
.Seek(s
.getPosition(), mins
* 60 + secs
);
1644 else if (boost::regex_match(spos
, what
, rx
.assign("([0-9]+)s"))) // position in seconds
1646 auto secs
= fromString
<unsigned>(what
[1]);
1647 Mpd
.Seek(s
.getPosition(), secs
);
1649 else if (boost::regex_match(spos
, what
, rx
.assign("([0-9]+)[%]{0,1}"))) // position in %
1651 auto percent
= fromString
<unsigned>(what
[1]);
1652 boundsCheck(percent
, 0u, 100u);
1653 int secs
= (percent
* s
.getDuration()) / 100.0;
1654 Mpd
.Seek(s
.getPosition(), secs
);
1657 Statusbar::print("Invalid format ([m]:[ss], [s]s, [%]%, [%] accepted)");
1660 bool SelectItem::canBeRun()
1662 m_list
= dynamic_cast<NC::List
*>(myScreen
->activeWindow());
1663 return m_list
!= nullptr
1665 && m_list
->currentP()->isSelectable();
1668 void SelectItem::run()
1670 auto current
= m_list
->currentP();
1671 current
->setSelected(!current
->isSelected());
1674 bool SelectRange::canBeRun()
1676 m_list
= dynamic_cast<NC::List
*>(myScreen
->activeWindow());
1677 if (m_list
== nullptr)
1679 m_begin
= m_list
->beginP();
1680 m_end
= m_list
->endP();
1681 return findRange(m_begin
, m_end
);
1684 void SelectRange::run()
1686 for (; m_begin
!= m_end
; ++m_begin
)
1687 m_begin
->setSelected(true);
1688 Statusbar::print("Range selected");
1691 bool ReverseSelection::canBeRun()
1693 m_list
= dynamic_cast<NC::List
*>(myScreen
->activeWindow());
1694 return m_list
!= nullptr;
1697 void ReverseSelection::run()
1699 for (auto &p
: *m_list
)
1700 p
.setSelected(!p
.isSelected());
1701 Statusbar::print("Selection reversed");
1704 bool RemoveSelection::canBeRun()
1706 m_list
= dynamic_cast<NC::List
*>(myScreen
->activeWindow());
1707 return m_list
!= nullptr;
1710 void RemoveSelection::run()
1712 for (auto &p
: *m_list
)
1713 p
.setSelected(false);
1714 Statusbar::print("Selection removed");
1717 bool SelectAlbum::canBeRun()
1719 auto *w
= myScreen
->activeWindow();
1720 if (m_list
!= static_cast<void *>(w
))
1721 m_list
= dynamic_cast<NC::List
*>(w
);
1722 if (m_songs
!= static_cast<void *>(w
))
1723 m_songs
= dynamic_cast<SongList
*>(w
);
1724 return m_list
!= nullptr && !m_list
->empty()
1725 && m_songs
!= nullptr;
1728 void SelectAlbum::run()
1730 const auto front
= m_songs
->beginS(), current
= m_songs
->currentS(), end
= m_songs
->endS();
1731 auto *s
= current
->get
<Bit::Song
>();
1734 auto get
= &MPD::Song::getAlbum
;
1735 const std::string tag
= s
->getTags(get
);
1737 for (auto it
= current
; it
!= front
;)
1740 s
= it
->get
<Bit::Song
>();
1741 if (s
== nullptr || s
->getTags(get
) != tag
)
1743 it
->get
<Bit::Properties
>().setSelected(true);
1746 for (auto it
= current
;;)
1748 it
->get
<Bit::Properties
>().setSelected(true);
1751 s
= it
->get
<Bit::Song
>();
1752 if (s
== nullptr || s
->getTags(get
) != tag
)
1755 Statusbar::print("Album around cursor position selected");
1758 bool AddSelectedItems::canBeRun()
1760 return myScreen
!= mySelectedItemsAdder
;
1763 void AddSelectedItems::run()
1765 mySelectedItemsAdder
->switchTo();
1768 void CropMainPlaylist::run()
1770 auto &w
= myPlaylist
->main();
1771 // cropping doesn't make sense in this case
1774 if (Config
.ask_before_clearing_playlists
)
1775 confirmAction("Do you really want to crop main playlist?");
1776 Statusbar::print("Cropping playlist...");
1777 selectCurrentIfNoneSelected(w
);
1778 cropPlaylist(w
, std::bind(&MPD::Connection::Delete
, ph::_1
, ph::_2
));
1779 Statusbar::print("Playlist cropped");
1782 bool CropPlaylist::canBeRun()
1784 return myScreen
== myPlaylistEditor
;
1787 void CropPlaylist::run()
1789 auto &w
= myPlaylistEditor
->Content
;
1790 // cropping doesn't make sense in this case
1793 assert(!myPlaylistEditor
->Playlists
.empty());
1794 std::string playlist
= myPlaylistEditor
->Playlists
.current()->value().path();
1795 if (Config
.ask_before_clearing_playlists
)
1796 confirmAction(boost::format("Do you really want to crop playlist \"%1%\"?") % playlist
);
1797 selectCurrentIfNoneSelected(w
);
1798 Statusbar::printf("Cropping playlist \"%1%\"...", playlist
);
1799 cropPlaylist(w
, std::bind(&MPD::Connection::PlaylistDelete
, ph::_1
, playlist
, ph::_2
));
1800 Statusbar::printf("Playlist \"%1%\" cropped", playlist
);
1803 void ClearMainPlaylist::run()
1805 if (!myPlaylist
->main().empty() && Config
.ask_before_clearing_playlists
)
1806 confirmAction("Do you really want to clear main playlist?");
1807 Mpd
.ClearMainPlaylist();
1808 Statusbar::print("Playlist cleared");
1809 myPlaylist
->main().reset();
1812 bool ClearPlaylist::canBeRun()
1814 return myScreen
== myPlaylistEditor
;
1817 void ClearPlaylist::run()
1819 if (myPlaylistEditor
->Playlists
.empty())
1821 std::string playlist
= myPlaylistEditor
->Playlists
.current()->value().path();
1822 if (Config
.ask_before_clearing_playlists
)
1823 confirmAction(boost::format("Do you really want to clear playlist \"%1%\"?") % playlist
);
1824 Mpd
.ClearPlaylist(playlist
);
1825 Statusbar::printf("Playlist \"%1%\" cleared", playlist
);
1828 bool SortPlaylist::canBeRun()
1830 if (myScreen
!= myPlaylist
)
1832 auto first
= myPlaylist
->main().begin(), last
= myPlaylist
->main().end();
1833 return findSelectedRangeAndPrintInfoIfNot(first
, last
);
1836 void SortPlaylist::run()
1838 mySortPlaylistDialog
->switchTo();
1841 bool ReversePlaylist::canBeRun()
1843 if (myScreen
!= myPlaylist
)
1845 m_begin
= myPlaylist
->main().begin();
1846 m_end
= myPlaylist
->main().end();
1847 return findSelectedRangeAndPrintInfoIfNot(m_begin
, m_end
);
1850 void ReversePlaylist::run()
1852 Statusbar::print("Reversing range...");
1853 Mpd
.StartCommandsList();
1854 for (--m_end
; m_begin
< m_end
; ++m_begin
, --m_end
)
1855 Mpd
.Swap(m_begin
->value().getPosition(), m_end
->value().getPosition());
1856 Mpd
.CommitCommandsList();
1857 Statusbar::print("Range reversed");
1860 bool Find::canBeRun()
1862 return myScreen
== myHelp
1863 || myScreen
== myLyrics
1864 # ifdef HAVE_CURL_CURL_H
1865 || myScreen
== myLastfm
1866 # endif // HAVE_CURL_CURL_H
1872 using Global::wFooter
;
1876 Statusbar::ScopedLock slock
;
1877 Statusbar::put() << "Find: ";
1878 token
= wFooter
->prompt();
1881 Statusbar::print("Searching...");
1882 auto s
= static_cast<Screen
<NC::Scrollpad
> *>(myScreen
);
1883 s
->main().removeProperties();
1884 if (token
.empty() || s
->main().setProperties(NC::Format::Reverse
, token
, NC::Format::NoReverse
, Config
.regex_type
))
1885 Statusbar::print("Done");
1887 Statusbar::print("No matching patterns found");
1891 bool FindItemBackward::canBeRun()
1893 auto w
= dynamic_cast<Searchable
*>(myScreen
);
1894 return w
&& w
->allowsSearching();
1897 void FindItemForward::run()
1899 findItem(SearchDirection::Forward
);
1900 listsChangeFinisher();
1903 bool FindItemForward::canBeRun()
1905 auto w
= dynamic_cast<Searchable
*>(myScreen
);
1906 return w
&& w
->allowsSearching();
1909 void FindItemBackward::run()
1911 findItem(SearchDirection::Backward
);
1912 listsChangeFinisher();
1915 bool NextFoundItem::canBeRun()
1917 return dynamic_cast<Searchable
*>(myScreen
);
1920 void NextFoundItem::run()
1922 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
1923 assert(w
!= nullptr);
1924 w
->find(SearchDirection::Forward
, Config
.wrapped_search
, true);
1925 listsChangeFinisher();
1928 bool PreviousFoundItem::canBeRun()
1930 return dynamic_cast<Searchable
*>(myScreen
);
1933 void PreviousFoundItem::run()
1935 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
1936 assert(w
!= nullptr);
1937 w
->find(SearchDirection::Backward
, Config
.wrapped_search
, true);
1938 listsChangeFinisher();
1941 void ToggleFindMode::run()
1943 Config
.wrapped_search
= !Config
.wrapped_search
;
1944 Statusbar::printf("Search mode: %1%",
1945 Config
.wrapped_search
? "Wrapped" : "Normal"
1949 void ToggleReplayGainMode::run()
1951 using Global::wFooter
;
1955 Statusbar::ScopedLock slock
;
1956 Statusbar::put() << "Replay gain mode? "
1957 << "[" << NC::Format::Bold
<< 'o' << NC::Format::NoBold
<< "ff"
1958 << "/" << NC::Format::Bold
<< 't' << NC::Format::NoBold
<< "rack"
1959 << "/" << NC::Format::Bold
<< 'a' << NC::Format::NoBold
<< "lbum"
1961 rgm
= Statusbar::Helpers::promptReturnOneOf({"t", "a", "o"})[0];
1966 Mpd
.SetReplayGainMode(MPD::rgmTrack
);
1969 Mpd
.SetReplayGainMode(MPD::rgmAlbum
);
1972 Mpd
.SetReplayGainMode(MPD::rgmOff
);
1974 default: // impossible
1975 throw std::runtime_error(
1976 (boost::format("ToggleReplayGainMode: impossible case reached: %1%") % rgm
).str()
1979 Statusbar::printf("Replay gain mode: %1%", Mpd
.GetReplayGainMode());
1982 void ToggleAddMode::run()
1984 std::string mode_desc
;
1985 switch (Config
.space_add_mode
)
1987 case SpaceAddMode::AddRemove
:
1988 Config
.space_add_mode
= SpaceAddMode::AlwaysAdd
;
1989 mode_desc
= "always add an item to playlist";
1991 case SpaceAddMode::AlwaysAdd
:
1992 Config
.space_add_mode
= SpaceAddMode::AddRemove
;
1993 mode_desc
= "add an item to playlist or remove if already added";
1996 Statusbar::printf("Add mode: %1%", mode_desc
);
1999 void ToggleMouse::run()
2001 Config
.mouse_support
= !Config
.mouse_support
;
2002 if (Config
.mouse_support
)
2003 NC::Mouse::enable();
2005 NC::Mouse::disable();
2006 Statusbar::printf("Mouse support %1%",
2007 Config
.mouse_support
? "enabled" : "disabled"
2011 void ToggleBitrateVisibility::run()
2013 Config
.display_bitrate
= !Config
.display_bitrate
;
2014 Statusbar::printf("Bitrate visibility %1%",
2015 Config
.display_bitrate
? "enabled" : "disabled"
2019 void AddRandomItems::run()
2021 using Global::wFooter
;
2024 Statusbar::ScopedLock slock
;
2025 Statusbar::put() << "Add random? "
2026 << "[" << NC::Format::Bold
<< 's' << NC::Format::NoBold
<< "ongs"
2027 << "/" << NC::Format::Bold
<< 'a' << NC::Format::NoBold
<< "rtists"
2028 << "/" << "album" << NC::Format::Bold
<< 'A' << NC::Format::NoBold
<< "rtists"
2029 << "/" << "al" << NC::Format::Bold
<< 'b' << NC::Format::NoBold
<< "ums"
2031 rnd_type
= Statusbar::Helpers::promptReturnOneOf({"s", "a", "A", "b"})[0];
2034 mpd_tag_type tag_type
= MPD_TAG_ARTIST
;
2035 std::string tag_type_str
;
2036 if (rnd_type
!= 's')
2038 tag_type
= charToTagType(rnd_type
);
2039 tag_type_str
= boost::locale::to_lower(tagTypeToString(tag_type
));
2042 tag_type_str
= "song";
2046 Statusbar::ScopedLock slock
;
2047 Statusbar::put() << "Number of random " << tag_type_str
<< "s: ";
2048 number
= fromString
<unsigned>(wFooter
->prompt());
2050 if (number
&& (rnd_type
== 's' ? Mpd
.AddRandomSongs(number
) : Mpd
.AddRandomTag(tag_type
, number
)))
2052 Statusbar::printf("%1% random %2%%3% added to playlist",
2053 number
, tag_type_str
, number
== 1 ? "" : "s"
2058 bool ToggleBrowserSortMode::canBeRun()
2060 return myScreen
== myBrowser
;
2063 void ToggleBrowserSortMode::run()
2065 switch (Config
.browser_sort_mode
)
2067 case SortMode::Name
:
2068 Config
.browser_sort_mode
= SortMode::ModificationTime
;
2069 Statusbar::print("Sort songs by: modification time");
2071 case SortMode::ModificationTime
:
2072 Config
.browser_sort_mode
= SortMode::CustomFormat
;
2073 Statusbar::print("Sort songs by: custom format");
2075 case SortMode::CustomFormat
:
2076 Config
.browser_sort_mode
= SortMode::NoOp
;
2077 Statusbar::print("Do not sort songs");
2079 case SortMode::NoOp
:
2080 Config
.browser_sort_mode
= SortMode::Name
;
2081 Statusbar::print("Sort songs by: name");
2083 if (Config
.browser_sort_mode
!= SortMode::NoOp
)
2085 size_t sort_offset
= myBrowser
->inRootDirectory() ? 0 : 1;
2086 std::sort(myBrowser
->main().begin()+sort_offset
, myBrowser
->main().end(),
2087 LocaleBasedItemSorting(std::locale(), Config
.ignore_leading_the
, Config
.browser_sort_mode
)
2092 bool ToggleLibraryTagType::canBeRun()
2094 return (myScreen
->isActiveWindow(myLibrary
->Tags
))
2095 || (myLibrary
->Columns() == 2 && myScreen
->isActiveWindow(myLibrary
->Albums
));
2098 void ToggleLibraryTagType::run()
2100 using Global::wFooter
;
2104 Statusbar::ScopedLock slock
;
2105 Statusbar::put() << "Tag type? "
2106 << "[" << NC::Format::Bold
<< 'a' << NC::Format::NoBold
<< "rtist"
2107 << "/" << "album" << NC::Format::Bold
<< 'A' << NC::Format::NoBold
<< "rtist"
2108 << "/" << NC::Format::Bold
<< 'y' << NC::Format::NoBold
<< "ear"
2109 << "/" << NC::Format::Bold
<< 'g' << NC::Format::NoBold
<< "enre"
2110 << "/" << NC::Format::Bold
<< 'c' << NC::Format::NoBold
<< "omposer"
2111 << "/" << NC::Format::Bold
<< 'p' << NC::Format::NoBold
<< "erformer"
2113 tag_type
= Statusbar::Helpers::promptReturnOneOf({"a", "A", "y", "g", "c", "p"})[0];
2115 mpd_tag_type new_tagitem
= charToTagType(tag_type
);
2116 if (new_tagitem
!= Config
.media_lib_primary_tag
)
2118 Config
.media_lib_primary_tag
= new_tagitem
;
2119 std::string item_type
= tagTypeToString(Config
.media_lib_primary_tag
);
2120 myLibrary
->Tags
.setTitle(Config
.titles_visibility
? item_type
+ "s" : "");
2121 myLibrary
->Tags
.reset();
2122 item_type
= boost::locale::to_lower(item_type
);
2123 std::string and_mtime
= Config
.media_library_sort_by_mtime
?
2126 if (myLibrary
->Columns() == 2)
2128 myLibrary
->Songs
.clear();
2129 myLibrary
->Albums
.reset();
2130 myLibrary
->Albums
.clear();
2131 myLibrary
->Albums
.setTitle(Config
.titles_visibility
? "Albums (sorted by " + item_type
+ and_mtime
+ ")" : "");
2132 myLibrary
->Albums
.display();
2136 myLibrary
->Tags
.clear();
2137 myLibrary
->Tags
.display();
2139 Statusbar::printf("Switched to the list of %1%s", item_type
);
2143 bool ToggleMediaLibrarySortMode::canBeRun()
2145 return myScreen
== myLibrary
;
2148 void ToggleMediaLibrarySortMode::run()
2150 myLibrary
->toggleSortMode();
2153 bool RefetchLyrics::canBeRun()
2155 # ifdef HAVE_CURL_CURL_H
2156 return myScreen
== myLyrics
;
2159 # endif // HAVE_CURL_CURL_H
2162 void RefetchLyrics::run()
2164 # ifdef HAVE_CURL_CURL_H
2165 myLyrics
->Refetch();
2166 # endif // HAVE_CURL_CURL_H
2169 bool SetSelectedItemsPriority::canBeRun()
2171 if (Mpd
.Version() < 17)
2173 Statusbar::print("Priorities are supported in MPD >= 0.17.0");
2176 return myScreen
== myPlaylist
&& !myPlaylist
->main().empty();
2179 void SetSelectedItemsPriority::run()
2181 using Global::wFooter
;
2185 Statusbar::ScopedLock slock
;
2186 Statusbar::put() << "Set priority [0-255]: ";
2187 prio
= fromString
<unsigned>(wFooter
->prompt());
2188 boundsCheck(prio
, 0u, 255u);
2190 myPlaylist
->SetSelectedItemsPriority(prio
);
2193 bool ToggleVisualizationType::canBeRun()
2195 # ifdef ENABLE_VISUALIZER
2196 return myScreen
== myVisualizer
;
2199 # endif // ENABLE_VISUALIZER
2202 void ToggleVisualizationType::run()
2204 # ifdef ENABLE_VISUALIZER
2205 myVisualizer
->ToggleVisualizationType();
2206 # endif // ENABLE_VISUALIZER
2209 bool SetVisualizerSampleMultiplier::canBeRun()
2211 # ifdef ENABLE_VISUALIZER
2212 return myScreen
== myVisualizer
;
2215 # endif // ENABLE_VISUALIZER
2218 void SetVisualizerSampleMultiplier::run()
2220 # ifdef ENABLE_VISUALIZER
2221 using Global::wFooter
;
2225 Statusbar::ScopedLock slock
;
2226 Statusbar::put() << "Set visualizer sample multiplier: ";
2227 multiplier
= fromString
<double>(wFooter
->prompt());
2228 lowerBoundCheck(multiplier
, 0.0);
2229 Config
.visualizer_sample_multiplier
= multiplier
;
2231 Statusbar::printf("Visualizer sample multiplier set to %1%", multiplier
);
2232 # endif // ENABLE_VISUALIZER
2235 void ShowSongInfo::run()
2237 mySongInfo
->switchTo();
2240 bool ShowArtistInfo::canBeRun()
2242 #ifdef HAVE_CURL_CURL_H
2243 return myScreen
== myLastfm
2244 || (myScreen
->isActiveWindow(myLibrary
->Tags
)
2245 && !myLibrary
->Tags
.empty()
2246 && Config
.media_lib_primary_tag
== MPD_TAG_ARTIST
)
2247 || currentSong(myScreen
);
2250 # endif // NOT HAVE_CURL_CURL_H
2253 void ShowArtistInfo::run()
2255 # ifdef HAVE_CURL_CURL_H
2256 if (myScreen
== myLastfm
)
2258 myLastfm
->switchTo();
2263 if (myScreen
->isActiveWindow(myLibrary
->Tags
))
2265 assert(!myLibrary
->Tags
.empty());
2266 assert(Config
.media_lib_primary_tag
== MPD_TAG_ARTIST
);
2267 artist
= myLibrary
->Tags
.current()->value().tag();
2271 auto s
= currentSong(myScreen
);
2273 artist
= s
->getArtist();
2276 if (!artist
.empty())
2278 myLastfm
->queueJob(new LastFm::ArtistInfo(artist
, Config
.lastfm_preferred_language
));
2279 myLastfm
->switchTo();
2281 # endif // HAVE_CURL_CURL_H
2284 void ShowLyrics::run()
2286 myLyrics
->switchTo();
2291 ExitMainLoop
= true;
2294 void NextScreen::run()
2296 if (Config
.screen_switcher_previous
)
2298 if (auto tababble
= dynamic_cast<Tabbable
*>(myScreen
))
2299 tababble
->switchToPreviousScreen();
2301 else if (!Config
.screen_sequence
.empty())
2303 const auto &seq
= Config
.screen_sequence
;
2304 auto screen_type
= std::find(seq
.begin(), seq
.end(), myScreen
->type());
2305 if (++screen_type
== seq
.end())
2306 toScreen(seq
.front())->switchTo();
2308 toScreen(*screen_type
)->switchTo();
2312 void PreviousScreen::run()
2314 if (Config
.screen_switcher_previous
)
2316 if (auto tababble
= dynamic_cast<Tabbable
*>(myScreen
))
2317 tababble
->switchToPreviousScreen();
2319 else if (!Config
.screen_sequence
.empty())
2321 const auto &seq
= Config
.screen_sequence
;
2322 auto screen_type
= std::find(seq
.begin(), seq
.end(), myScreen
->type());
2323 if (screen_type
== seq
.begin())
2324 toScreen(seq
.back())->switchTo();
2326 toScreen(*--screen_type
)->switchTo();
2330 bool ShowHelp::canBeRun()
2332 return myScreen
!= myHelp
2333 # ifdef HAVE_TAGLIB_H
2334 && myScreen
!= myTinyTagEditor
2335 # endif // HAVE_TAGLIB_H
2339 void ShowHelp::run()
2344 bool ShowPlaylist::canBeRun()
2346 return myScreen
!= myPlaylist
2347 # ifdef HAVE_TAGLIB_H
2348 && myScreen
!= myTinyTagEditor
2349 # endif // HAVE_TAGLIB_H
2353 void ShowPlaylist::run()
2355 myPlaylist
->switchTo();
2358 bool ShowBrowser::canBeRun()
2360 return myScreen
!= myBrowser
2361 # ifdef HAVE_TAGLIB_H
2362 && myScreen
!= myTinyTagEditor
2363 # endif // HAVE_TAGLIB_H
2367 void ShowBrowser::run()
2369 myBrowser
->switchTo();
2372 bool ChangeBrowseMode::canBeRun()
2374 return myScreen
== myBrowser
;
2377 void ChangeBrowseMode::run()
2379 myBrowser
->changeBrowseMode();
2382 bool ShowSearchEngine::canBeRun()
2384 return myScreen
!= mySearcher
2385 # ifdef HAVE_TAGLIB_H
2386 && myScreen
!= myTinyTagEditor
2387 # endif // HAVE_TAGLIB_H
2391 void ShowSearchEngine::run()
2393 mySearcher
->switchTo();
2396 bool ResetSearchEngine::canBeRun()
2398 return myScreen
== mySearcher
;
2401 void ResetSearchEngine::run()
2403 mySearcher
->reset();
2406 bool ShowMediaLibrary::canBeRun()
2408 return myScreen
!= myLibrary
2409 # ifdef HAVE_TAGLIB_H
2410 && myScreen
!= myTinyTagEditor
2411 # endif // HAVE_TAGLIB_H
2415 void ShowMediaLibrary::run()
2417 myLibrary
->switchTo();
2420 bool ToggleMediaLibraryColumnsMode::canBeRun()
2422 return myScreen
== myLibrary
;
2425 void ToggleMediaLibraryColumnsMode::run()
2427 myLibrary
->toggleColumnsMode();
2428 myLibrary
->refresh();
2431 bool ShowPlaylistEditor::canBeRun()
2433 return myScreen
!= myPlaylistEditor
2434 # ifdef HAVE_TAGLIB_H
2435 && myScreen
!= myTinyTagEditor
2436 # endif // HAVE_TAGLIB_H
2440 void ShowPlaylistEditor::run()
2442 myPlaylistEditor
->switchTo();
2445 bool ShowTagEditor::canBeRun()
2447 # ifdef HAVE_TAGLIB_H
2448 return myScreen
!= myTagEditor
2449 && myScreen
!= myTinyTagEditor
;
2452 # endif // HAVE_TAGLIB_H
2455 void ShowTagEditor::run()
2457 # ifdef HAVE_TAGLIB_H
2458 if (isMPDMusicDirSet())
2459 myTagEditor
->switchTo();
2460 # endif // HAVE_TAGLIB_H
2463 bool ShowOutputs::canBeRun()
2465 # ifdef ENABLE_OUTPUTS
2466 return myScreen
!= myOutputs
2467 # ifdef HAVE_TAGLIB_H
2468 && myScreen
!= myTinyTagEditor
2469 # endif // HAVE_TAGLIB_H
2473 # endif // ENABLE_OUTPUTS
2476 void ShowOutputs::run()
2478 # ifdef ENABLE_OUTPUTS
2479 myOutputs
->switchTo();
2480 # endif // ENABLE_OUTPUTS
2483 bool ShowVisualizer::canBeRun()
2485 # ifdef ENABLE_VISUALIZER
2486 return myScreen
!= myVisualizer
2487 # ifdef HAVE_TAGLIB_H
2488 && myScreen
!= myTinyTagEditor
2489 # endif // HAVE_TAGLIB_H
2493 # endif // ENABLE_VISUALIZER
2496 void ShowVisualizer::run()
2498 # ifdef ENABLE_VISUALIZER
2499 myVisualizer
->switchTo();
2500 # endif // ENABLE_VISUALIZER
2503 bool ShowClock::canBeRun()
2505 # ifdef ENABLE_CLOCK
2506 return myScreen
!= myClock
2507 # ifdef HAVE_TAGLIB_H
2508 && myScreen
!= myTinyTagEditor
2509 # endif // HAVE_TAGLIB_H
2513 # endif // ENABLE_CLOCK
2516 void ShowClock::run()
2518 # ifdef ENABLE_CLOCK
2519 myClock
->switchTo();
2520 # endif // ENABLE_CLOCK
2523 #ifdef HAVE_TAGLIB_H
2524 bool ShowServerInfo::canBeRun()
2526 return myScreen
!= myTinyTagEditor
;
2528 #endif // HAVE_TAGLIB_H
2530 void ShowServerInfo::run()
2532 myServerInfo
->switchTo();
2539 void populateActions()
2541 auto insert_action
= [](Actions::BaseAction
*a
) {
2542 AvailableActions
[static_cast<size_t>(a
->type())] = a
;
2544 insert_action(new Actions::Dummy());
2545 insert_action(new Actions::UpdateEnvironment());
2546 insert_action(new Actions::MouseEvent());
2547 insert_action(new Actions::ScrollUp());
2548 insert_action(new Actions::ScrollDown());
2549 insert_action(new Actions::ScrollUpArtist());
2550 insert_action(new Actions::ScrollUpAlbum());
2551 insert_action(new Actions::ScrollDownArtist());
2552 insert_action(new Actions::ScrollDownAlbum());
2553 insert_action(new Actions::PageUp());
2554 insert_action(new Actions::PageDown());
2555 insert_action(new Actions::MoveHome());
2556 insert_action(new Actions::MoveEnd());
2557 insert_action(new Actions::ToggleInterface());
2558 insert_action(new Actions::JumpToParentDirectory());
2559 insert_action(new Actions::PressEnter());
2560 insert_action(new Actions::SelectItem());
2561 insert_action(new Actions::SelectRange());
2562 insert_action(new Actions::PreviousColumn());
2563 insert_action(new Actions::NextColumn());
2564 insert_action(new Actions::MasterScreen());
2565 insert_action(new Actions::SlaveScreen());
2566 insert_action(new Actions::VolumeUp());
2567 insert_action(new Actions::VolumeDown());
2568 insert_action(new Actions::AddItemToPlaylist());
2569 insert_action(new Actions::DeletePlaylistItems());
2570 insert_action(new Actions::DeleteStoredPlaylist());
2571 insert_action(new Actions::DeleteBrowserItems());
2572 insert_action(new Actions::ReplaySong());
2573 insert_action(new Actions::PreviousSong());
2574 insert_action(new Actions::NextSong());
2575 insert_action(new Actions::Pause());
2576 insert_action(new Actions::Stop());
2577 insert_action(new Actions::ExecuteCommand());
2578 insert_action(new Actions::SavePlaylist());
2579 insert_action(new Actions::MoveSortOrderUp());
2580 insert_action(new Actions::MoveSortOrderDown());
2581 insert_action(new Actions::MoveSelectedItemsUp());
2582 insert_action(new Actions::MoveSelectedItemsDown());
2583 insert_action(new Actions::MoveSelectedItemsTo());
2584 insert_action(new Actions::Add());
2585 insert_action(new Actions::SeekForward());
2586 insert_action(new Actions::SeekBackward());
2587 insert_action(new Actions::ToggleDisplayMode());
2588 insert_action(new Actions::ToggleSeparatorsBetweenAlbums());
2589 insert_action(new Actions::ToggleLyricsUpdateOnSongChange());
2590 insert_action(new Actions::ToggleLyricsFetcher());
2591 insert_action(new Actions::ToggleFetchingLyricsInBackground());
2592 insert_action(new Actions::TogglePlayingSongCentering());
2593 insert_action(new Actions::UpdateDatabase());
2594 insert_action(new Actions::JumpToPlayingSong());
2595 insert_action(new Actions::ToggleRepeat());
2596 insert_action(new Actions::Shuffle());
2597 insert_action(new Actions::ToggleRandom());
2598 insert_action(new Actions::StartSearching());
2599 insert_action(new Actions::SaveTagChanges());
2600 insert_action(new Actions::ToggleSingle());
2601 insert_action(new Actions::ToggleConsume());
2602 insert_action(new Actions::ToggleCrossfade());
2603 insert_action(new Actions::SetCrossfade());
2604 insert_action(new Actions::SetVolume());
2605 insert_action(new Actions::EditSong());
2606 insert_action(new Actions::EditLibraryTag());
2607 insert_action(new Actions::EditLibraryAlbum());
2608 insert_action(new Actions::EditDirectoryName());
2609 insert_action(new Actions::EditPlaylistName());
2610 insert_action(new Actions::EditLyrics());
2611 insert_action(new Actions::JumpToBrowser());
2612 insert_action(new Actions::JumpToMediaLibrary());
2613 insert_action(new Actions::JumpToPlaylistEditor());
2614 insert_action(new Actions::ToggleScreenLock());
2615 insert_action(new Actions::JumpToTagEditor());
2616 insert_action(new Actions::JumpToPositionInSong());
2617 insert_action(new Actions::ReverseSelection());
2618 insert_action(new Actions::RemoveSelection());
2619 insert_action(new Actions::SelectAlbum());
2620 insert_action(new Actions::AddSelectedItems());
2621 insert_action(new Actions::CropMainPlaylist());
2622 insert_action(new Actions::CropPlaylist());
2623 insert_action(new Actions::ClearMainPlaylist());
2624 insert_action(new Actions::ClearPlaylist());
2625 insert_action(new Actions::SortPlaylist());
2626 insert_action(new Actions::ReversePlaylist());
2627 insert_action(new Actions::Find());
2628 insert_action(new Actions::FindItemForward());
2629 insert_action(new Actions::FindItemBackward());
2630 insert_action(new Actions::NextFoundItem());
2631 insert_action(new Actions::PreviousFoundItem());
2632 insert_action(new Actions::ToggleFindMode());
2633 insert_action(new Actions::ToggleReplayGainMode());
2634 insert_action(new Actions::ToggleAddMode());
2635 insert_action(new Actions::ToggleMouse());
2636 insert_action(new Actions::ToggleBitrateVisibility());
2637 insert_action(new Actions::AddRandomItems());
2638 insert_action(new Actions::ToggleBrowserSortMode());
2639 insert_action(new Actions::ToggleLibraryTagType());
2640 insert_action(new Actions::ToggleMediaLibrarySortMode());
2641 insert_action(new Actions::RefetchLyrics());
2642 insert_action(new Actions::SetSelectedItemsPriority());
2643 insert_action(new Actions::ToggleVisualizationType());
2644 insert_action(new Actions::SetVisualizerSampleMultiplier());
2645 insert_action(new Actions::ShowSongInfo());
2646 insert_action(new Actions::ShowArtistInfo());
2647 insert_action(new Actions::ShowLyrics());
2648 insert_action(new Actions::Quit());
2649 insert_action(new Actions::NextScreen());
2650 insert_action(new Actions::PreviousScreen());
2651 insert_action(new Actions::ShowHelp());
2652 insert_action(new Actions::ShowPlaylist());
2653 insert_action(new Actions::ShowBrowser());
2654 insert_action(new Actions::ChangeBrowseMode());
2655 insert_action(new Actions::ShowSearchEngine());
2656 insert_action(new Actions::ResetSearchEngine());
2657 insert_action(new Actions::ShowMediaLibrary());
2658 insert_action(new Actions::ToggleMediaLibraryColumnsMode());
2659 insert_action(new Actions::ShowPlaylistEditor());
2660 insert_action(new Actions::ShowTagEditor());
2661 insert_action(new Actions::ShowOutputs());
2662 insert_action(new Actions::ShowVisualizer());
2663 insert_action(new Actions::ShowClock());
2664 insert_action(new Actions::ShowServerInfo());
2667 bool scrollTagCanBeRun(NC::List
*&list
, SongList
*&songs
)
2669 auto w
= myScreen
->activeWindow();
2670 if (list
!= static_cast<void *>(w
))
2671 list
= dynamic_cast<NC::List
*>(w
);
2672 if (songs
!= static_cast<void *>(w
))
2673 songs
= dynamic_cast<SongList
*>(w
);
2674 return list
!= nullptr && !list
->empty()
2675 && songs
!= nullptr;
2678 void scrollTagUpRun(NC::List
*list
, SongList
*songs
, MPD::Song::GetFunction get
)
2680 const auto front
= songs
->beginS();
2681 auto it
= songs
->currentS();
2682 if (auto *s
= it
->get
<Bit::Song
>())
2684 const std::string tag
= s
->getTags(get
);
2688 s
= it
->get
<Bit::Song
>();
2689 if (s
== nullptr || s
->getTags(get
) != tag
)
2692 list
->highlight(it
-front
);
2696 void scrollTagDownRun(NC::List
*list
, SongList
*songs
, MPD::Song::GetFunction get
)
2698 const auto front
= songs
->beginS(), back
= --songs
->endS();
2699 auto it
= songs
->currentS();
2700 if (auto *s
= it
->get
<Bit::Song
>())
2702 const std::string tag
= s
->getTags(get
);
2706 s
= it
->get
<Bit::Song
>();
2707 if (s
== nullptr || s
->getTags(get
) != tag
)
2710 list
->highlight(it
-front
);
2716 using Global::wHeader
;
2717 using Global::wFooter
;
2718 using Global::Timer
;
2719 using Global::SeekingInProgress
;
2721 if (!Status::State::totalTime())
2723 Statusbar::print("Unknown item length");
2727 Progressbar::ScopedLock progressbar_lock
;
2728 Statusbar::ScopedLock statusbar_lock
;
2730 unsigned songpos
= Status::State::elapsedTime();
2733 int old_timeout
= wFooter
->getTimeout();
2734 wFooter
->setTimeout(BaseScreen::defaultWindowTimeout
);
2736 auto seekForward
= &Actions::get(Actions::Type::SeekForward
);
2737 auto seekBackward
= &Actions::get(Actions::Type::SeekBackward
);
2739 SeekingInProgress
= true;
2744 unsigned howmuch
= Config
.incremental_seeking
2745 ? (Timer
-t
).total_seconds()/2+Config
.seek_time
2748 NC::Key::Type input
= readKey(*wFooter
);
2749 auto k
= Bindings
.get(input
);
2750 if (k
.first
== k
.second
|| !k
.first
->isSingle()) // no single action?
2752 auto a
= k
.first
->action();
2753 if (a
== seekForward
)
2755 if (songpos
< Status::State::totalTime())
2756 songpos
= std::min(songpos
+ howmuch
, Status::State::totalTime());
2758 else if (a
== seekBackward
)
2762 if (songpos
< howmuch
)
2771 *wFooter
<< NC::Format::Bold
;
2772 std::string tracklength
;
2773 // FIXME: merge this with the code in status.cpp
2774 switch (Config
.design
)
2776 case Design::Classic
:
2778 if (Config
.display_remaining_time
)
2781 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime()-songpos
);
2784 tracklength
+= MPD::Song::ShowTime(songpos
);
2786 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime());
2788 *wFooter
<< NC::XY(wFooter
->getWidth()-tracklength
.length(), 1) << tracklength
;
2790 case Design::Alternative
:
2791 if (Config
.display_remaining_time
)
2794 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime()-songpos
);
2797 tracklength
= MPD::Song::ShowTime(songpos
);
2799 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime());
2800 *wHeader
<< NC::XY(0, 0) << tracklength
<< " ";
2804 *wFooter
<< NC::Format::NoBold
;
2805 Progressbar::draw(songpos
, Status::State::totalTime());
2808 SeekingInProgress
= false;
2809 Mpd
.Seek(Status::State::currentSongPosition(), songpos
);
2811 wFooter
->setTimeout(old_timeout
);
2814 void findItem(const SearchDirection direction
)
2816 using Global::wFooter
;
2818 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
2819 assert(w
!= nullptr);
2820 assert(w
->allowsSearching());
2822 std::string constraint
;
2824 Statusbar::ScopedLock slock
;
2825 NC::Window::ScopedPromptHook
prompt_hook(*wFooter
,
2826 Statusbar::Helpers::FindImmediately(w
, direction
)
2828 Statusbar::put() << (boost::format("Find %1%: ") % direction
).str();
2829 constraint
= wFooter
->prompt();
2834 if (constraint
.empty())
2836 Statusbar::printf("Constraint unset");
2837 w
->clearConstraint();
2841 w
->setSearchConstraint(constraint
);
2842 Statusbar::printf("Using constraint \"%1%\"", constraint
);
2845 catch (boost::bad_expression
&e
)
2847 Statusbar::printf("%1%", e
.what());
2851 void listsChangeFinisher()
2853 if (myScreen
== myLibrary
2854 || myScreen
== myPlaylistEditor
2855 # ifdef HAVE_TAGLIB_H
2856 || myScreen
== myTagEditor
2857 # endif // HAVE_TAGLIB_H
2860 if (myScreen
->activeWindow() == &myLibrary
->Tags
)
2862 myLibrary
->Albums
.clear();
2863 myLibrary
->Albums
.refresh();
2864 myLibrary
->Songs
.clear();
2865 myLibrary
->Songs
.refresh();
2866 myLibrary
->updateTimer();
2868 else if (myScreen
->activeWindow() == &myLibrary
->Albums
)
2870 myLibrary
->Songs
.clear();
2871 myLibrary
->Songs
.refresh();
2872 myLibrary
->updateTimer();
2874 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
))
2876 myPlaylistEditor
->Content
.clear();
2877 myPlaylistEditor
->Content
.refresh();
2878 myPlaylistEditor
->updateTimer();
2880 # ifdef HAVE_TAGLIB_H
2881 else if (myScreen
->activeWindow() == myTagEditor
->Dirs
)
2883 myTagEditor
->Tags
->clear();
2885 # endif // HAVE_TAGLIB_H