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();
96 bool OriginalStatusbarVisibility
;
97 bool ExitMainLoop
= false;
103 void validateScreenSize()
105 using Global::MainHeight
;
107 if (COLS
< 30 || MainHeight
< 5)
110 std::cout
<< "Screen is too small to handle ncmpcpp correctly\n";
115 void initializeScreens()
118 myPlaylist
= new Playlist
;
119 myBrowser
= new Browser
;
120 mySearcher
= new SearchEngine
;
121 myLibrary
= new MediaLibrary
;
122 myPlaylistEditor
= new PlaylistEditor
;
123 myLyrics
= new Lyrics
;
124 mySelectedItemsAdder
= new SelectedItemsAdder
;
125 mySongInfo
= new SongInfo
;
126 myServerInfo
= new ServerInfo
;
127 mySortPlaylistDialog
= new SortPlaylistDialog
;
129 # ifdef HAVE_CURL_CURL_H
130 myLastfm
= new Lastfm
;
131 # endif // HAVE_CURL_CURL_H
133 # ifdef HAVE_TAGLIB_H
134 myTinyTagEditor
= new TinyTagEditor
;
135 myTagEditor
= new TagEditor
;
136 # endif // HAVE_TAGLIB_H
138 # ifdef ENABLE_VISUALIZER
139 myVisualizer
= new Visualizer
;
140 # endif // ENABLE_VISUALIZER
142 # ifdef ENABLE_OUTPUTS
143 myOutputs
= new Outputs
;
144 # endif // ENABLE_OUTPUTS
148 # endif // ENABLE_CLOCK
152 void setResizeFlags()
154 myHelp
->hasToBeResized
= 1;
155 myPlaylist
->hasToBeResized
= 1;
156 myBrowser
->hasToBeResized
= 1;
157 mySearcher
->hasToBeResized
= 1;
158 myLibrary
->hasToBeResized
= 1;
159 myPlaylistEditor
->hasToBeResized
= 1;
160 myLyrics
->hasToBeResized
= 1;
161 mySelectedItemsAdder
->hasToBeResized
= 1;
162 mySongInfo
->hasToBeResized
= 1;
163 myServerInfo
->hasToBeResized
= 1;
164 mySortPlaylistDialog
->hasToBeResized
= 1;
166 # ifdef HAVE_CURL_CURL_H
167 myLastfm
->hasToBeResized
= 1;
168 # endif // HAVE_CURL_CURL_H
170 # ifdef HAVE_TAGLIB_H
171 myTinyTagEditor
->hasToBeResized
= 1;
172 myTagEditor
->hasToBeResized
= 1;
173 # endif // HAVE_TAGLIB_H
175 # ifdef ENABLE_VISUALIZER
176 myVisualizer
->hasToBeResized
= 1;
177 # endif // ENABLE_VISUALIZER
179 # ifdef ENABLE_OUTPUTS
180 myOutputs
->hasToBeResized
= 1;
181 # endif // ENABLE_OUTPUTS
184 myClock
->hasToBeResized
= 1;
185 # endif // ENABLE_CLOCK
188 void resizeScreen(bool reload_main_window
)
190 using Global::MainHeight
;
191 using Global::wHeader
;
192 using Global::wFooter
;
194 // update internal screen dimensions
195 if (reload_main_window
)
197 rl_resize_terminal();
202 MainHeight
= LINES
-(Config
.design
== Design::Alternative
? 7 : 4);
204 validateScreenSize();
206 if (!Config
.header_visibility
)
208 if (!Config
.statusbar_visibility
)
213 applyToVisibleWindows(&BaseScreen::resize
);
215 if (Config
.header_visibility
|| Config
.design
== Design::Alternative
)
216 wHeader
->resize(COLS
, HeaderHeight
);
218 FooterStartY
= LINES
-(Config
.statusbar_visibility
? 2 : 1);
219 wFooter
->moveTo(0, FooterStartY
);
220 wFooter
->resize(COLS
, Config
.statusbar_visibility
? 2 : 1);
222 applyToVisibleWindows(&BaseScreen::refresh
);
224 Status::Changes::elapsedTime(false);
225 Status::Changes::playerState();
226 // Note: routines for drawing separator if alternative user
227 // interface is active and header is hidden are placed in
228 // NcmpcppStatusChanges.StatusFlags
229 Status::Changes::flags();
235 void setWindowsDimensions()
237 using Global::MainStartY
;
238 using Global::MainHeight
;
240 MainStartY
= Config
.design
== Design::Alternative
? 5 : 2;
241 MainHeight
= LINES
-(Config
.design
== Design::Alternative
? 7 : 4);
243 if (!Config
.header_visibility
)
248 if (!Config
.statusbar_visibility
)
251 HeaderHeight
= Config
.design
== Design::Alternative
? (Config
.header_visibility
? 5 : 3) : 1;
252 FooterStartY
= LINES
-(Config
.statusbar_visibility
? 2 : 1);
253 FooterHeight
= Config
.statusbar_visibility
? 2 : 1;
256 void confirmAction(const boost::format
&description
)
258 Statusbar::ScopedLock slock
;
259 Statusbar::put() << description
.str()
260 << " [" << NC::Format::Bold
<< 'y' << NC::Format::NoBold
261 << '/' << NC::Format::Bold
<< 'n' << NC::Format::NoBold
263 auto answer
= Statusbar::Helpers::promptReturnOneOf({"y", "n"});
265 throw NC::PromptAborted(std::move(answer
));
268 bool isMPDMusicDirSet()
270 if (Config
.mpd_music_dir
.empty())
272 Statusbar::print("Proper mpd_music_dir variable has to be set in configuration file");
278 BaseAction
&get(Actions::Type at
)
280 if (AvailableActions
[1] == nullptr)
282 BaseAction
*action
= AvailableActions
[static_cast<size_t>(at
)];
283 // action should be always present if action type in queried
284 assert(action
!= nullptr);
288 BaseAction
*get(const std::string
&name
)
290 BaseAction
*result
= 0;
291 if (AvailableActions
[1] == nullptr)
293 for (auto it
= AvailableActions
.begin(); it
!= AvailableActions
.end(); ++it
)
295 if (*it
!= nullptr && (*it
)->name() == name
)
304 bool MouseEvent::canBeRun()
306 return Config
.mouse_support
;
309 void MouseEvent::run()
311 using Global::VolumeState
;
312 using Global::wFooter
;
314 m_old_mouse_event
= m_mouse_event
;
315 m_mouse_event
= wFooter
->getMouseEvent();
317 //Statusbar::printf("(%1%, %2%, %3%)", m_mouse_event.bstate, m_mouse_event.x, m_mouse_event.y);
319 if (m_mouse_event
.bstate
& BUTTON1_PRESSED
320 && m_mouse_event
.y
== LINES
-(Config
.statusbar_visibility
? 2 : 1)
323 if (Status::State::player() == MPD::psStop
)
325 Mpd
.Seek(Status::State::currentSongPosition(),
326 Status::State::totalTime()*m_mouse_event
.x
/double(COLS
));
328 else if (m_mouse_event
.bstate
& BUTTON1_PRESSED
329 && (Config
.statusbar_visibility
|| Config
.design
== Design::Alternative
)
330 && Status::State::player() != MPD::psStop
331 && m_mouse_event
.y
== (Config
.design
== Design::Alternative
? 1 : LINES
-1)
332 && m_mouse_event
.x
< 9
337 else if ((m_mouse_event
.bstate
& BUTTON5_PRESSED
|| m_mouse_event
.bstate
& BUTTON4_PRESSED
)
338 && (Config
.header_visibility
|| Config
.design
== Design::Alternative
)
339 && m_mouse_event
.y
== 0 && size_t(m_mouse_event
.x
) > COLS
-VolumeState
.length()
342 if (m_mouse_event
.bstate
& BUTTON5_PRESSED
)
343 get(Type::VolumeDown
).execute();
345 get(Type::VolumeUp
).execute();
347 else if (m_mouse_event
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
| BUTTON4_PRESSED
| BUTTON5_PRESSED
))
348 myScreen
->mouseButtonPressed(m_mouse_event
);
353 myScreen
->scroll(NC::Scroll::Up
);
354 listsChangeFinisher();
357 void ScrollDown::run()
359 myScreen
->scroll(NC::Scroll::Down
);
360 listsChangeFinisher();
363 bool ScrollUpArtist::canBeRun()
365 return scrollTagCanBeRun(m_list
, m_songs
);
368 void ScrollUpArtist::run()
370 scrollTagUpRun(m_list
, m_songs
, &MPD::Song::getArtist
);
373 bool ScrollUpAlbum::canBeRun()
375 return scrollTagCanBeRun(m_list
, m_songs
);
378 void ScrollUpAlbum::run()
380 scrollTagUpRun(m_list
, m_songs
, &MPD::Song::getAlbum
);
383 bool ScrollDownArtist::canBeRun()
385 return scrollTagCanBeRun(m_list
, m_songs
);
388 void ScrollDownArtist::run()
390 scrollTagDownRun(m_list
, m_songs
, &MPD::Song::getArtist
);
393 bool ScrollDownAlbum::canBeRun()
395 return scrollTagCanBeRun(m_list
, m_songs
);
398 void ScrollDownAlbum::run()
400 scrollTagDownRun(m_list
, m_songs
, &MPD::Song::getAlbum
);
405 myScreen
->scroll(NC::Scroll::PageUp
);
406 listsChangeFinisher();
411 myScreen
->scroll(NC::Scroll::PageDown
);
412 listsChangeFinisher();
417 myScreen
->scroll(NC::Scroll::Home
);
418 listsChangeFinisher();
423 myScreen
->scroll(NC::Scroll::End
);
424 listsChangeFinisher();
427 void ToggleInterface::run()
429 switch (Config
.design
)
431 case Design::Classic
:
432 Config
.design
= Design::Alternative
;
433 Config
.statusbar_visibility
= false;
435 case Design::Alternative
:
436 Config
.design
= Design::Classic
;
437 Config
.statusbar_visibility
= OriginalStatusbarVisibility
;
440 setWindowsDimensions();
442 // unlock progressbar
443 Progressbar::ScopedLock();
444 Status::Changes::mixer();
445 Status::Changes::elapsedTime(false);
446 Statusbar::printf("User interface: %1%", Config
.design
);
449 bool JumpToParentDirectory::canBeRun()
451 return (myScreen
== myBrowser
)
452 # ifdef HAVE_TAGLIB_H
453 || (myScreen
->activeWindow() == myTagEditor
->Dirs
)
454 # endif // HAVE_TAGLIB_H
458 void JumpToParentDirectory::run()
460 if (myScreen
== myBrowser
)
462 if (!myBrowser
->inRootDirectory())
464 myBrowser
->main().reset();
465 myBrowser
->enterPressed();
468 # ifdef HAVE_TAGLIB_H
469 else if (myScreen
== myTagEditor
)
471 if (myTagEditor
->CurrentDir() != "/")
473 myTagEditor
->Dirs
->reset();
474 myTagEditor
->enterPressed();
477 # endif // HAVE_TAGLIB_H
480 void PressEnter::run()
482 myScreen
->enterPressed();
485 void PressSpace::run()
487 myScreen
->spacePressed();
490 bool PreviousColumn::canBeRun()
492 auto hc
= hasColumns(myScreen
);
493 return hc
&& hc
->previousColumnAvailable();
496 void PreviousColumn::run()
498 hasColumns(myScreen
)->previousColumn();
501 bool NextColumn::canBeRun()
503 auto hc
= hasColumns(myScreen
);
504 return hc
&& hc
->nextColumnAvailable();
507 void NextColumn::run()
509 hasColumns(myScreen
)->nextColumn();
512 bool MasterScreen::canBeRun()
514 using Global::myLockedScreen
;
515 using Global::myInactiveScreen
;
517 return myLockedScreen
519 && myLockedScreen
!= myScreen
520 && myScreen
->isMergable();
523 void MasterScreen::run()
525 using Global::myInactiveScreen
;
526 using Global::myLockedScreen
;
528 myInactiveScreen
= myScreen
;
529 myScreen
= myLockedScreen
;
533 bool SlaveScreen::canBeRun()
535 using Global::myLockedScreen
;
536 using Global::myInactiveScreen
;
538 return myLockedScreen
540 && myLockedScreen
== myScreen
541 && myScreen
->isMergable();
544 void SlaveScreen::run()
546 using Global::myInactiveScreen
;
547 using Global::myLockedScreen
;
549 myScreen
= myInactiveScreen
;
550 myInactiveScreen
= myLockedScreen
;
556 int volume
= std::min(Status::State::volume()+Config
.volume_change_step
, 100u);
557 Mpd
.SetVolume(volume
);
560 void VolumeDown::run()
562 int volume
= std::max(int(Status::State::volume()-Config
.volume_change_step
), 0);
563 Mpd
.SetVolume(volume
);
566 bool DeletePlaylistItems::canBeRun()
568 return (myScreen
== myPlaylist
&& !myPlaylist
->main().empty())
569 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
) && !myPlaylistEditor
->Content
.empty());
572 void DeletePlaylistItems::run()
574 if (myScreen
== myPlaylist
)
576 Statusbar::print("Deleting items...");
577 auto delete_fun
= std::bind(&MPD::Connection::Delete
, ph::_1
, ph::_2
);
578 deleteSelectedSongs(myPlaylist
->main(), delete_fun
);
579 Statusbar::print("Item(s) deleted");
581 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Content
))
583 std::string playlist
= myPlaylistEditor
->Playlists
.current()->value().path();
584 auto delete_fun
= std::bind(&MPD::Connection::PlaylistDelete
, ph::_1
, playlist
, ph::_2
);
585 Statusbar::print("Deleting items...");
586 deleteSelectedSongs(myPlaylistEditor
->Content
, delete_fun
);
587 Statusbar::print("Item(s) deleted");
591 bool DeleteBrowserItems::canBeRun()
593 auto check_if_deletion_allowed
= []() {
594 if (Config
.allow_for_physical_item_deletion
)
598 Statusbar::print("Flag \"allow_for_physical_item_deletion\" needs to be enabled in configuration file");
602 return myScreen
== myBrowser
603 && !myBrowser
->main().empty()
604 && isMPDMusicDirSet()
605 && check_if_deletion_allowed();
608 void DeleteBrowserItems::run()
610 auto get_name
= [](const MPD::Item
&item
) -> std::string
{
614 case MPD::Item::Type::Directory
:
615 iname
= getBasename(item
.directory().path());
617 case MPD::Item::Type::Song
:
618 iname
= item
.song().getName();
620 case MPD::Item::Type::Playlist
:
621 iname
= getBasename(item
.playlist().path());
627 boost::format question
;
628 if (hasSelected(myBrowser
->main().begin(), myBrowser
->main().end()))
629 question
= boost::format("Delete selected items?");
632 const auto &item
= myBrowser
->main().current()->value();
633 // parent directories are not accepted (and they
634 // can't be selected, so in other cases it's fine).
635 if (myBrowser
->isParentDirectory(item
))
637 const char msg
[] = "Delete \"%1%\"?";
638 question
= boost::format(msg
) % wideShorten(
639 get_name(item
), COLS
-const_strlen(msg
)-5
642 confirmAction(question
);
644 auto items
= getSelectedOrCurrent(
645 myBrowser
->main().begin(),
646 myBrowser
->main().end(),
647 myBrowser
->main().current()
649 for (const auto &item
: items
)
651 myBrowser
->remove(item
->value());
652 const char msg
[] = "Deleted %1% \"%2%\"";
653 Statusbar::printf(msg
,
654 itemTypeToString(item
->value().type()),
655 wideShorten(get_name(item
->value()), COLS
-const_strlen(msg
))
659 if (!myBrowser
->isLocal())
660 Mpd
.UpdateDirectory(myBrowser
->currentDirectory());
661 myBrowser
->requestUpdate();
664 bool DeleteStoredPlaylist::canBeRun()
666 return myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
);
669 void DeleteStoredPlaylist::run()
671 if (myPlaylistEditor
->Playlists
.empty())
673 boost::format question
;
674 if (hasSelected(myPlaylistEditor
->Playlists
.begin(), myPlaylistEditor
->Playlists
.end()))
675 question
= boost::format("Delete selected playlists?");
677 question
= boost::format("Delete playlist \"%1%\"?")
678 % wideShorten(myPlaylistEditor
->Playlists
.current()->value().path(), COLS
-question
.size()-10);
679 confirmAction(question
);
680 auto list
= getSelectedOrCurrent(
681 myPlaylistEditor
->Playlists
.begin(),
682 myPlaylistEditor
->Playlists
.end(),
683 myPlaylistEditor
->Playlists
.current()
685 for (const auto &item
: list
)
686 Mpd
.DeletePlaylist(item
->value().path());
687 Statusbar::printf("%1% deleted", list
.size() == 1 ? "Playlist" : "Playlists");
688 // force playlists update. this happens automatically, but only after call
689 // to Key::read, therefore when we call PlaylistEditor::Update, it won't
690 // yet see it, so let's point that it needs to update it.
691 myPlaylistEditor
->requestPlaylistsUpdate();
694 void ReplaySong::run()
696 if (Status::State::player() != MPD::psStop
)
697 Mpd
.Seek(Status::State::currentSongPosition(), 0);
700 void PreviousSong::run()
715 void SavePlaylist::run()
717 using Global::wFooter
;
719 std::string playlist_name
;
721 Statusbar::ScopedLock slock
;
722 Statusbar::put() << "Save playlist as: ";
723 playlist_name
= wFooter
->prompt();
727 Mpd
.SavePlaylist(playlist_name
);
728 Statusbar::printf("Playlist saved as \"%1%\"", playlist_name
);
730 catch (MPD::ServerError
&e
)
732 if (e
.code() == MPD_SERVER_ERROR_EXIST
)
735 boost::format("Playlist \"%1%\" already exists, overwrite?") % playlist_name
737 Mpd
.DeletePlaylist(playlist_name
);
738 Mpd
.SavePlaylist(playlist_name
);
739 Statusbar::print("Playlist overwritten");
751 void ExecuteCommand::run()
753 using Global::wFooter
;
755 std::string cmd_name
;
757 Statusbar::ScopedLock slock
;
758 NC::Window::ScopedPromptHook
helper(*wFooter
,
759 Statusbar::Helpers::TryExecuteImmediateCommand()
761 Statusbar::put() << NC::Format::Bold
<< ":" << NC::Format::NoBold
;
762 cmd_name
= wFooter
->prompt();
765 auto cmd
= Bindings
.findCommand(cmd_name
);
768 Statusbar::printf(1, "Executing %1%...", cmd_name
);
769 bool res
= cmd
->binding().execute();
770 Statusbar::printf("Execution of command \"%1%\" %2%.",
771 cmd_name
, res
? "successful" : "unsuccessful"
775 Statusbar::printf("No command named \"%1%\"", cmd_name
);
778 bool MoveSortOrderUp::canBeRun()
780 return myScreen
== mySortPlaylistDialog
;
783 void MoveSortOrderUp::run()
785 mySortPlaylistDialog
->moveSortOrderUp();
788 bool MoveSortOrderDown::canBeRun()
790 return myScreen
== mySortPlaylistDialog
;
793 void MoveSortOrderDown::run()
795 mySortPlaylistDialog
->moveSortOrderDown();
798 bool MoveSelectedItemsUp::canBeRun()
800 return ((myScreen
== myPlaylist
801 && !myPlaylist
->main().empty())
802 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
)
803 && !myPlaylistEditor
->Content
.empty()));
806 void MoveSelectedItemsUp::run()
808 if (myScreen
== myPlaylist
)
810 moveSelectedItemsUp(myPlaylist
->main(), std::bind(&MPD::Connection::Move
, ph::_1
, ph::_2
, ph::_3
));
812 else if (myScreen
== myPlaylistEditor
)
814 assert(!myPlaylistEditor
->Playlists
.empty());
815 std::string playlist
= myPlaylistEditor
->Playlists
.current()->value().path();
816 auto move_fun
= std::bind(&MPD::Connection::PlaylistMove
, ph::_1
, playlist
, ph::_2
, ph::_3
);
817 moveSelectedItemsUp(myPlaylistEditor
->Content
, move_fun
);
821 bool MoveSelectedItemsDown::canBeRun()
823 return ((myScreen
== myPlaylist
824 && !myPlaylist
->main().empty())
825 || (myScreen
->isActiveWindow(myPlaylistEditor
->Content
)
826 && !myPlaylistEditor
->Content
.empty()));
829 void MoveSelectedItemsDown::run()
831 if (myScreen
== myPlaylist
)
833 moveSelectedItemsDown(myPlaylist
->main(), std::bind(&MPD::Connection::Move
, ph::_1
, ph::_2
, ph::_3
));
835 else if (myScreen
== myPlaylistEditor
)
837 assert(!myPlaylistEditor
->Playlists
.empty());
838 std::string playlist
= myPlaylistEditor
->Playlists
.current()->value().path();
839 auto move_fun
= std::bind(&MPD::Connection::PlaylistMove
, ph::_1
, playlist
, ph::_2
, ph::_3
);
840 moveSelectedItemsDown(myPlaylistEditor
->Content
, move_fun
);
844 bool MoveSelectedItemsTo::canBeRun()
846 return myScreen
== myPlaylist
847 || myScreen
->isActiveWindow(myPlaylistEditor
->Content
);
850 void MoveSelectedItemsTo::run()
852 if (myScreen
== myPlaylist
)
854 if (!myPlaylist
->main().empty())
855 moveSelectedItemsTo(myPlaylist
->main(), std::bind(&MPD::Connection::Move
, ph::_1
, ph::_2
, ph::_3
));
859 assert(!myPlaylistEditor
->Playlists
.empty());
860 std::string playlist
= myPlaylistEditor
->Playlists
.current()->value().path();
861 auto move_fun
= std::bind(&MPD::Connection::PlaylistMove
, ph::_1
, playlist
, ph::_2
, ph::_3
);
862 moveSelectedItemsTo(myPlaylistEditor
->Content
, move_fun
);
868 return myScreen
!= myPlaylistEditor
869 || !myPlaylistEditor
->Playlists
.empty();
874 using Global::wFooter
;
878 Statusbar::ScopedLock slock
;
879 Statusbar::put() << (myScreen
== myPlaylistEditor
? "Add to playlist: " : "Add: ");
880 path
= wFooter
->prompt();
883 // confirm when one wants to add the whole database
885 confirmAction("Are you sure you want to add the whole database?");
887 Statusbar::put() << "Adding...";
889 if (myScreen
== myPlaylistEditor
)
890 Mpd
.AddToPlaylist(myPlaylistEditor
->Playlists
.current()->value().path(), path
);
897 catch (MPD::ServerError
&err
)
899 // If a path is not a file or directory, assume it is a playlist.
900 if (err
.code() == MPD_SERVER_ERROR_NO_EXIST
)
901 Mpd
.LoadPlaylist(path
);
908 bool SeekForward::canBeRun()
910 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
913 void SeekForward::run()
918 bool SeekBackward::canBeRun()
920 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
923 void SeekBackward::run()
928 bool ToggleDisplayMode::canBeRun()
930 return myScreen
== myPlaylist
931 || myScreen
== myBrowser
932 || myScreen
== mySearcher
933 || myScreen
->isActiveWindow(myPlaylistEditor
->Content
);
936 void ToggleDisplayMode::run()
938 if (myScreen
== myPlaylist
)
940 switch (Config
.playlist_display_mode
)
942 case DisplayMode::Classic
:
943 Config
.playlist_display_mode
= DisplayMode::Columns
;
944 myPlaylist
->main().setItemDisplayer(std::bind(
945 Display::SongsInColumns
, ph::_1
, std::cref(myPlaylist
->main())
947 if (Config
.titles_visibility
)
948 myPlaylist
->main().setTitle(Display::Columns(myPlaylist
->main().getWidth()));
950 myPlaylist
->main().setTitle("");
952 case DisplayMode::Columns
:
953 Config
.playlist_display_mode
= DisplayMode::Classic
;
954 myPlaylist
->main().setItemDisplayer(std::bind(
955 Display::Songs
, ph::_1
, std::cref(myPlaylist
->main()), std::cref(Config
.song_list_format
)
957 myPlaylist
->main().setTitle("");
959 Statusbar::printf("Playlist display mode: %1%", Config
.playlist_display_mode
);
961 else if (myScreen
== myBrowser
)
963 switch (Config
.browser_display_mode
)
965 case DisplayMode::Classic
:
966 Config
.browser_display_mode
= DisplayMode::Columns
;
967 if (Config
.titles_visibility
)
968 myBrowser
->main().setTitle(Display::Columns(myBrowser
->main().getWidth()));
970 myBrowser
->main().setTitle("");
972 case DisplayMode::Columns
:
973 Config
.browser_display_mode
= DisplayMode::Classic
;
974 myBrowser
->main().setTitle("");
977 Statusbar::printf("Browser display mode: %1%", Config
.browser_display_mode
);
979 else if (myScreen
== mySearcher
)
981 switch (Config
.search_engine_display_mode
)
983 case DisplayMode::Classic
:
984 Config
.search_engine_display_mode
= DisplayMode::Columns
;
986 case DisplayMode::Columns
:
987 Config
.search_engine_display_mode
= DisplayMode::Classic
;
990 Statusbar::printf("Search engine display mode: %1%", Config
.search_engine_display_mode
);
991 if (mySearcher
->main().size() > SearchEngine::StaticOptions
)
992 mySearcher
->main().setTitle(
993 Config
.search_engine_display_mode
== DisplayMode::Columns
994 && Config
.titles_visibility
995 ? Display::Columns(mySearcher
->main().getWidth())
999 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Content
))
1001 switch (Config
.playlist_editor_display_mode
)
1003 case DisplayMode::Classic
:
1004 Config
.playlist_editor_display_mode
= DisplayMode::Columns
;
1005 myPlaylistEditor
->Content
.setItemDisplayer(std::bind(
1006 Display::SongsInColumns
, ph::_1
, std::cref(myPlaylistEditor
->Content
)
1009 case DisplayMode::Columns
:
1010 Config
.playlist_editor_display_mode
= DisplayMode::Classic
;
1011 myPlaylistEditor
->Content
.setItemDisplayer(std::bind(
1012 Display::Songs
, ph::_1
, std::cref(myPlaylistEditor
->Content
), std::cref(Config
.song_list_format
)
1016 Statusbar::printf("Playlist editor display mode: %1%", Config
.playlist_editor_display_mode
);
1020 bool ToggleSeparatorsBetweenAlbums::canBeRun()
1025 void ToggleSeparatorsBetweenAlbums::run()
1027 Config
.playlist_separate_albums
= !Config
.playlist_separate_albums
;
1028 Statusbar::printf("Separators between albums: %1%",
1029 Config
.playlist_separate_albums
? "on" : "off"
1033 #ifndef HAVE_CURL_CURL_H
1034 bool ToggleLyricsFetcher::canBeRun()
1038 #endif // NOT HAVE_CURL_CURL_H
1040 void ToggleLyricsFetcher::run()
1042 # ifdef HAVE_CURL_CURL_H
1043 myLyrics
->ToggleFetcher();
1044 # endif // HAVE_CURL_CURL_H
1047 #ifndef HAVE_CURL_CURL_H
1048 bool ToggleFetchingLyricsInBackground::canBeRun()
1052 #endif // NOT HAVE_CURL_CURL_H
1054 void ToggleFetchingLyricsInBackground::run()
1056 # ifdef HAVE_CURL_CURL_H
1057 Config
.fetch_lyrics_in_background
= !Config
.fetch_lyrics_in_background
;
1058 Statusbar::printf("Fetching lyrics for playing songs in background: %1%",
1059 Config
.fetch_lyrics_in_background
? "on" : "off"
1061 # endif // HAVE_CURL_CURL_H
1064 void TogglePlayingSongCentering::run()
1066 Config
.autocenter_mode
= !Config
.autocenter_mode
;
1067 Statusbar::printf("Centering playing song: %1%",
1068 Config
.autocenter_mode
? "on" : "off"
1070 if (Config
.autocenter_mode
)
1072 auto s
= myPlaylist
->nowPlayingSong();
1074 myPlaylist
->main().highlight(s
.getPosition());
1078 void UpdateDatabase::run()
1080 if (myScreen
== myBrowser
)
1081 Mpd
.UpdateDirectory(myBrowser
->currentDirectory());
1082 # ifdef HAVE_TAGLIB_H
1083 else if (myScreen
== myTagEditor
)
1084 Mpd
.UpdateDirectory(myTagEditor
->CurrentDir());
1085 # endif // HAVE_TAGLIB_H
1087 Mpd
.UpdateDirectory("/");
1090 bool JumpToPlayingSong::canBeRun()
1092 return myScreen
== myPlaylist
1093 || myScreen
== myBrowser
1094 || myScreen
== myLibrary
;
1097 void JumpToPlayingSong::run()
1099 auto s
= myPlaylist
->nowPlayingSong();
1102 if (myScreen
== myPlaylist
)
1104 myPlaylist
->main().highlight(s
.getPosition());
1106 else if (myScreen
== myBrowser
)
1108 myBrowser
->locateSong(s
);
1110 else if (myScreen
== myLibrary
)
1112 myLibrary
->LocateSong(s
);
1116 void ToggleRepeat::run()
1118 Mpd
.SetRepeat(!Status::State::repeat());
1123 auto begin
= myPlaylist
->main().begin(), end
= myPlaylist
->main().end();
1124 auto range
= getSelectedRange(begin
, end
);
1125 Mpd
.ShuffleRange(range
.first
-begin
, range
.second
-begin
);
1126 Statusbar::print("Range shuffled");
1129 void ToggleRandom::run()
1131 Mpd
.SetRandom(!Status::State::random());
1134 bool StartSearching::canBeRun()
1136 return myScreen
== mySearcher
&& !mySearcher
->main()[0].isInactive();
1139 void StartSearching::run()
1141 mySearcher
->main().highlight(SearchEngine::SearchButton
);
1142 mySearcher
->main().setHighlighting(0);
1143 mySearcher
->main().refresh();
1144 mySearcher
->main().setHighlighting(1);
1145 mySearcher
->enterPressed();
1148 bool SaveTagChanges::canBeRun()
1150 # ifdef HAVE_TAGLIB_H
1151 return myScreen
== myTinyTagEditor
1152 || myScreen
->activeWindow() == myTagEditor
->TagTypes
;
1155 # endif // HAVE_TAGLIB_H
1158 void SaveTagChanges::run()
1160 # ifdef HAVE_TAGLIB_H
1161 if (myScreen
== myTinyTagEditor
)
1163 myTinyTagEditor
->main().highlight(myTinyTagEditor
->main().size()-2); // Save
1164 myTinyTagEditor
->enterPressed();
1166 else if (myScreen
->activeWindow() == myTagEditor
->TagTypes
)
1168 myTagEditor
->TagTypes
->highlight(myTagEditor
->TagTypes
->size()-1); // Save
1169 myTagEditor
->enterPressed();
1171 # endif // HAVE_TAGLIB_H
1174 void ToggleSingle::run()
1176 Mpd
.SetSingle(!Status::State::single());
1179 void ToggleConsume::run()
1181 Mpd
.SetConsume(!Status::State::consume());
1184 void ToggleCrossfade::run()
1186 Mpd
.SetCrossfade(Status::State::crossfade() ? 0 : Config
.crossfade_time
);
1189 void SetCrossfade::run()
1191 using Global::wFooter
;
1193 Statusbar::ScopedLock slock
;
1194 Statusbar::put() << "Set crossfade to: ";
1195 auto crossfade
= fromString
<unsigned>(wFooter
->prompt());
1196 lowerBoundCheck(crossfade
, 0u);
1197 Config
.crossfade_time
= crossfade
;
1198 Mpd
.SetCrossfade(crossfade
);
1201 void SetVolume::run()
1203 using Global::wFooter
;
1207 Statusbar::ScopedLock slock
;
1208 Statusbar::put() << "Set volume to: ";
1209 volume
= fromString
<unsigned>(wFooter
->prompt());
1210 boundsCheck(volume
, 0u, 100u);
1211 Mpd
.SetVolume(volume
);
1213 Statusbar::printf("Volume set to %1%%%", volume
);
1216 bool EditSong::canBeRun()
1218 # ifdef HAVE_TAGLIB_H
1219 m_song
= currentSong(myScreen
);
1220 return m_song
!= nullptr && isMPDMusicDirSet();
1223 # endif // HAVE_TAGLIB_H
1226 void EditSong::run()
1228 # ifdef HAVE_TAGLIB_H
1229 myTinyTagEditor
->SetEdited(*m_song
);
1230 myTinyTagEditor
->switchTo();
1231 # endif // HAVE_TAGLIB_H
1234 bool EditLibraryTag::canBeRun()
1236 # ifdef HAVE_TAGLIB_H
1237 return myScreen
->isActiveWindow(myLibrary
->Tags
)
1238 && !myLibrary
->Tags
.empty()
1239 && isMPDMusicDirSet();
1242 # endif // HAVE_TAGLIB_H
1245 void EditLibraryTag::run()
1247 # ifdef HAVE_TAGLIB_H
1248 using Global::wFooter
;
1250 std::string new_tag
;
1252 Statusbar::ScopedLock slock
;
1253 Statusbar::put() << NC::Format::Bold
<< tagTypeToString(Config
.media_lib_primary_tag
) << NC::Format::NoBold
<< ": ";
1254 new_tag
= wFooter
->prompt(myLibrary
->Tags
.current()->value().tag());
1256 if (!new_tag
.empty() && new_tag
!= myLibrary
->Tags
.current()->value().tag())
1258 Statusbar::print("Updating tags...");
1259 Mpd
.StartSearch(true);
1260 Mpd
.AddSearch(Config
.media_lib_primary_tag
, myLibrary
->Tags
.current()->value().tag());
1261 MPD::MutableSong::SetFunction set
= tagTypeToSetFunction(Config
.media_lib_primary_tag
);
1263 bool success
= true;
1264 std::string dir_to_update
;
1265 for (MPD::SongIterator s
= Mpd
.CommitSearchSongs(), end
; s
!= end
; ++s
)
1267 MPD::MutableSong ms
= std::move(*s
);
1268 ms
.setTags(set
, new_tag
);
1269 Statusbar::printf("Updating tags in \"%1%\"...", ms
.getName());
1270 std::string path
= Config
.mpd_music_dir
+ ms
.getURI();
1271 if (!Tags::write(ms
))
1274 const char msg
[] = "Error while updating tags in \"%1%\"";
1275 Statusbar::printf(msg
, wideShorten(ms
.getURI(), COLS
-const_strlen(msg
)));
1279 if (dir_to_update
.empty())
1280 dir_to_update
= ms
.getURI();
1282 dir_to_update
= getSharedDirectory(dir_to_update
, ms
.getURI());
1286 Mpd
.UpdateDirectory(dir_to_update
);
1287 Statusbar::print("Tags updated successfully");
1290 # endif // HAVE_TAGLIB_H
1293 bool EditLibraryAlbum::canBeRun()
1295 # ifdef HAVE_TAGLIB_H
1296 return myScreen
->isActiveWindow(myLibrary
->Albums
)
1297 && !myLibrary
->Albums
.empty()
1298 && isMPDMusicDirSet();
1301 # endif // HAVE_TAGLIB_H
1304 void EditLibraryAlbum::run()
1306 # ifdef HAVE_TAGLIB_H
1307 using Global::wFooter
;
1308 // FIXME: merge this and EditLibraryTag. also, prompt on failure if user wants to continue
1309 std::string new_album
;
1311 Statusbar::ScopedLock slock
;
1312 Statusbar::put() << NC::Format::Bold
<< "Album: " << NC::Format::NoBold
;
1313 new_album
= wFooter
->prompt(myLibrary
->Albums
.current()->value().entry().album());
1315 if (!new_album
.empty() && new_album
!= myLibrary
->Albums
.current()->value().entry().album())
1318 Statusbar::print("Updating tags...");
1319 for (size_t i
= 0; i
< myLibrary
->Songs
.size(); ++i
)
1321 Statusbar::printf("Updating tags in \"%1%\"...", myLibrary
->Songs
[i
].value().getName());
1322 std::string path
= Config
.mpd_music_dir
+ myLibrary
->Songs
[i
].value().getURI();
1323 TagLib::FileRef
f(path
.c_str());
1326 const char msg
[] = "Error while opening file \"%1%\"";
1327 Statusbar::printf(msg
, wideShorten(myLibrary
->Songs
[i
].value().getURI(), COLS
-const_strlen(msg
)));
1331 f
.tag()->setAlbum(ToWString(new_album
));
1334 const char msg
[] = "Error while writing tags in \"%1%\"";
1335 Statusbar::printf(msg
, wideShorten(myLibrary
->Songs
[i
].value().getURI(), COLS
-const_strlen(msg
)));
1342 Mpd
.UpdateDirectory(getSharedDirectory(myLibrary
->Songs
.beginV(), myLibrary
->Songs
.endV()));
1343 Statusbar::print("Tags updated successfully");
1346 # endif // HAVE_TAGLIB_H
1349 bool EditDirectoryName::canBeRun()
1351 return ((myScreen
== myBrowser
1352 && !myBrowser
->main().empty()
1353 && myBrowser
->main().current()->value().type() == MPD::Item::Type::Directory
)
1354 # ifdef HAVE_TAGLIB_H
1355 || (myScreen
->activeWindow() == myTagEditor
->Dirs
1356 && !myTagEditor
->Dirs
->empty()
1357 && myTagEditor
->Dirs
->choice() > 0)
1358 # endif // HAVE_TAGLIB_H
1359 ) && isMPDMusicDirSet();
1362 void EditDirectoryName::run()
1364 using Global::wFooter
;
1365 if (myScreen
== myBrowser
)
1367 std::string old_dir
= myBrowser
->main().current()->value().directory().path();
1368 std::string new_dir
;
1370 Statusbar::ScopedLock slock
;
1371 Statusbar::put() << NC::Format::Bold
<< "Directory: " << NC::Format::NoBold
;
1372 new_dir
= wFooter
->prompt(old_dir
);
1374 if (!new_dir
.empty() && new_dir
!= old_dir
)
1376 std::string full_old_dir
;
1377 if (!myBrowser
->isLocal())
1378 full_old_dir
+= Config
.mpd_music_dir
;
1379 full_old_dir
+= old_dir
;
1380 std::string full_new_dir
;
1381 if (!myBrowser
->isLocal())
1382 full_new_dir
+= Config
.mpd_music_dir
;
1383 full_new_dir
+= new_dir
;
1384 boost::filesystem::rename(full_old_dir
, full_new_dir
);
1385 const char msg
[] = "Directory renamed to \"%1%\"";
1386 Statusbar::printf(msg
, wideShorten(new_dir
, COLS
-const_strlen(msg
)));
1387 if (!myBrowser
->isLocal())
1388 Mpd
.UpdateDirectory(getSharedDirectory(old_dir
, new_dir
));
1389 myBrowser
->requestUpdate();
1392 # ifdef HAVE_TAGLIB_H
1393 else if (myScreen
->activeWindow() == myTagEditor
->Dirs
)
1395 std::string old_dir
= myTagEditor
->Dirs
->current()->value().first
, new_dir
;
1397 Statusbar::ScopedLock slock
;
1398 Statusbar::put() << NC::Format::Bold
<< "Directory: " << NC::Format::NoBold
;
1399 new_dir
= wFooter
->prompt(old_dir
);
1401 if (!new_dir
.empty() && new_dir
!= old_dir
)
1403 std::string full_old_dir
= Config
.mpd_music_dir
+ myTagEditor
->CurrentDir() + "/" + old_dir
;
1404 std::string full_new_dir
= Config
.mpd_music_dir
+ myTagEditor
->CurrentDir() + "/" + new_dir
;
1405 if (rename(full_old_dir
.c_str(), full_new_dir
.c_str()) == 0)
1407 const char msg
[] = "Directory renamed to \"%1%\"";
1408 Statusbar::printf(msg
, wideShorten(new_dir
, COLS
-const_strlen(msg
)));
1409 Mpd
.UpdateDirectory(myTagEditor
->CurrentDir());
1413 const char msg
[] = "Couldn't rename \"%1%\": %2%";
1414 Statusbar::printf(msg
, wideShorten(old_dir
, COLS
-const_strlen(msg
)-25), strerror(errno
));
1418 # endif // HAVE_TAGLIB_H
1421 bool EditPlaylistName::canBeRun()
1423 return (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
)
1424 && !myPlaylistEditor
->Playlists
.empty())
1425 || (myScreen
== myBrowser
1426 && !myBrowser
->main().empty()
1427 && myBrowser
->main().current()->value().type() == MPD::Item::Type::Playlist
);
1430 void EditPlaylistName::run()
1432 using Global::wFooter
;
1433 std::string old_name
, new_name
;
1434 if (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
))
1435 old_name
= myPlaylistEditor
->Playlists
.current()->value().path();
1437 old_name
= myBrowser
->main().current()->value().playlist().path();
1439 Statusbar::ScopedLock slock
;
1440 Statusbar::put() << NC::Format::Bold
<< "Playlist: " << NC::Format::NoBold
;
1441 new_name
= wFooter
->prompt(old_name
);
1443 if (!new_name
.empty() && new_name
!= old_name
)
1445 Mpd
.Rename(old_name
, new_name
);
1446 const char msg
[] = "Playlist renamed to \"%1%\"";
1447 Statusbar::printf(msg
, wideShorten(new_name
, COLS
-const_strlen(msg
)));
1451 bool EditLyrics::canBeRun()
1453 return myScreen
== myLyrics
;
1456 void EditLyrics::run()
1461 bool JumpToBrowser::canBeRun()
1463 m_song
= currentSong(myScreen
);
1464 return m_song
!= nullptr;
1467 void JumpToBrowser::run()
1469 myBrowser
->locateSong(*m_song
);
1472 bool JumpToMediaLibrary::canBeRun()
1474 m_song
= currentSong(myScreen
);
1475 return m_song
!= nullptr;
1478 void JumpToMediaLibrary::run()
1480 myLibrary
->LocateSong(*m_song
);
1483 bool JumpToPlaylistEditor::canBeRun()
1485 return myScreen
== myBrowser
1486 && myBrowser
->main().current()->value().type() == MPD::Item::Type::Playlist
;
1489 void JumpToPlaylistEditor::run()
1491 myPlaylistEditor
->Locate(myBrowser
->main().current()->value().playlist());
1494 void ToggleScreenLock::run()
1496 using Global::wFooter
;
1497 using Global::myLockedScreen
;
1498 const char *msg_unlockable_screen
= "Current screen can't be locked";
1499 if (myLockedScreen
!= nullptr)
1501 BaseScreen::unlock();
1502 Actions::setResizeFlags();
1504 Statusbar::print("Screen unlocked");
1506 else if (!myScreen
->isLockable())
1508 Statusbar::print(msg_unlockable_screen
);
1512 unsigned part
= Config
.locked_screen_width_part
*100;
1513 if (Config
.ask_for_locked_screen_width_part
)
1515 Statusbar::ScopedLock slock
;
1516 Statusbar::put() << "% of the locked screen's width to be reserved (20-80): ";
1517 part
= fromString
<unsigned>(wFooter
->prompt(boost::lexical_cast
<std::string
>(part
)));
1519 boundsCheck(part
, 20u, 80u);
1520 Config
.locked_screen_width_part
= part
/100.0;
1521 if (myScreen
->lock())
1522 Statusbar::printf("Screen locked (with %1%%% width)", part
);
1524 Statusbar::print(msg_unlockable_screen
);
1528 bool JumpToTagEditor::canBeRun()
1530 # ifdef HAVE_TAGLIB_H
1531 m_song
= currentSong(myScreen
);
1532 return m_song
!= nullptr && isMPDMusicDirSet();
1535 # endif // HAVE_TAGLIB_H
1538 void JumpToTagEditor::run()
1540 # ifdef HAVE_TAGLIB_H
1541 myTagEditor
->LocateSong(*m_song
);
1542 # endif // HAVE_TAGLIB_H
1545 bool JumpToPositionInSong::canBeRun()
1547 return Status::State::player() != MPD::psStop
&& Status::State::totalTime() > 0;
1550 void JumpToPositionInSong::run()
1552 using Global::wFooter
;
1554 const MPD::Song s
= myPlaylist
->nowPlayingSong();
1558 Statusbar::ScopedLock slock
;
1559 Statusbar::put() << "Position to go (in %/m:ss/seconds(s)): ";
1560 spos
= wFooter
->prompt();
1565 if (boost::regex_match(spos
, what
, rx
.assign("([0-9]+):([0-9]{2})"))) // mm:ss
1567 auto mins
= fromString
<unsigned>(what
[1]);
1568 auto secs
= fromString
<unsigned>(what
[2]);
1569 boundsCheck(secs
, 0u, 60u);
1570 Mpd
.Seek(s
.getPosition(), mins
* 60 + secs
);
1572 else if (boost::regex_match(spos
, what
, rx
.assign("([0-9]+)s"))) // position in seconds
1574 auto secs
= fromString
<unsigned>(what
[1]);
1575 Mpd
.Seek(s
.getPosition(), secs
);
1577 else if (boost::regex_match(spos
, what
, rx
.assign("([0-9]+)[%]{0,1}"))) // position in %
1579 auto percent
= fromString
<unsigned>(what
[1]);
1580 boundsCheck(percent
, 0u, 100u);
1581 int secs
= (percent
* s
.getDuration()) / 100.0;
1582 Mpd
.Seek(s
.getPosition(), secs
);
1585 Statusbar::print("Invalid format ([m]:[ss], [s]s, [%]%, [%] accepted)");
1588 bool SelectItem::canBeRun()
1590 m_list
= dynamic_cast<NC::List
*>(myScreen
->activeWindow());
1591 return m_list
!= nullptr
1593 && m_list
->currentP()->isSelectable();
1596 void SelectItem::run()
1598 auto current
= m_list
->currentP();
1599 current
->setSelected(!current
->isSelected());
1600 myScreen
->scroll(NC::Scroll::Down
);
1601 listsChangeFinisher();
1604 bool ReverseSelection::canBeRun()
1606 m_list
= dynamic_cast<NC::List
*>(myScreen
->activeWindow());
1607 return m_list
!= nullptr;
1610 void ReverseSelection::run()
1612 for (auto &p
: *m_list
)
1613 p
.setSelected(!p
.isSelected());
1614 Statusbar::print("Selection reversed");
1617 bool RemoveSelection::canBeRun()
1619 m_list
= dynamic_cast<NC::List
*>(myScreen
->activeWindow());
1620 return m_list
!= nullptr;
1623 void RemoveSelection::run()
1625 for (auto &p
: *m_list
)
1626 p
.setSelected(false);
1627 Statusbar::print("Selection removed");
1630 bool SelectAlbum::canBeRun()
1632 auto *w
= myScreen
->activeWindow();
1633 if (m_list
!= static_cast<void *>(w
))
1634 m_list
= dynamic_cast<NC::List
*>(w
);
1635 if (m_songs
!= static_cast<void *>(w
))
1636 m_songs
= dynamic_cast<SongList
*>(w
);
1637 return m_list
!= nullptr && !m_list
->empty()
1638 && m_songs
!= nullptr;
1641 void SelectAlbum::run()
1643 const auto front
= m_songs
->beginS(), current
= m_songs
->currentS(), end
= m_songs
->endS();
1644 auto *s
= current
->get
<Bit::Song
>();
1647 auto get
= &MPD::Song::getAlbum
;
1648 const std::string tag
= s
->getTags(get
);
1650 for (auto it
= current
; it
!= front
;)
1653 s
= it
->get
<Bit::Song
>();
1654 if (s
== nullptr || s
->getTags(get
) != tag
)
1656 it
->get
<Bit::Properties
>().setSelected(true);
1659 for (auto it
= current
;;)
1661 it
->get
<Bit::Properties
>().setSelected(true);
1664 s
= it
->get
<Bit::Song
>();
1665 if (s
== nullptr || s
->getTags(get
) != tag
)
1668 Statusbar::print("Album around cursor position selected");
1671 bool AddSelectedItems::canBeRun()
1673 return myScreen
!= mySelectedItemsAdder
;
1676 void AddSelectedItems::run()
1678 mySelectedItemsAdder
->switchTo();
1681 void CropMainPlaylist::run()
1683 auto &w
= myPlaylist
->main();
1684 // cropping doesn't make sense in this case
1687 if (Config
.ask_before_clearing_playlists
)
1688 confirmAction("Do you really want to crop main playlist?");
1689 Statusbar::print("Cropping playlist...");
1690 selectCurrentIfNoneSelected(w
);
1691 cropPlaylist(w
, std::bind(&MPD::Connection::Delete
, ph::_1
, ph::_2
));
1692 Statusbar::print("Playlist cropped");
1695 bool CropPlaylist::canBeRun()
1697 return myScreen
== myPlaylistEditor
;
1700 void CropPlaylist::run()
1702 auto &w
= myPlaylistEditor
->Content
;
1703 // cropping doesn't make sense in this case
1706 assert(!myPlaylistEditor
->Playlists
.empty());
1707 std::string playlist
= myPlaylistEditor
->Playlists
.current()->value().path();
1708 if (Config
.ask_before_clearing_playlists
)
1709 confirmAction(boost::format("Do you really want to crop playlist \"%1%\"?") % playlist
);
1710 selectCurrentIfNoneSelected(w
);
1711 Statusbar::printf("Cropping playlist \"%1%\"...", playlist
);
1712 cropPlaylist(w
, std::bind(&MPD::Connection::PlaylistDelete
, ph::_1
, playlist
, ph::_2
));
1713 Statusbar::printf("Playlist \"%1%\" cropped", playlist
);
1716 void ClearMainPlaylist::run()
1718 if (!myPlaylist
->main().empty() && Config
.ask_before_clearing_playlists
)
1719 confirmAction("Do you really want to clear main playlist?");
1720 Mpd
.ClearMainPlaylist();
1721 Statusbar::print("Playlist cleared");
1722 myPlaylist
->main().reset();
1725 bool ClearPlaylist::canBeRun()
1727 return myScreen
== myPlaylistEditor
;
1730 void ClearPlaylist::run()
1732 if (myPlaylistEditor
->Playlists
.empty())
1734 std::string playlist
= myPlaylistEditor
->Playlists
.current()->value().path();
1735 if (Config
.ask_before_clearing_playlists
)
1736 confirmAction(boost::format("Do you really want to clear playlist \"%1%\"?") % playlist
);
1737 Mpd
.ClearPlaylist(playlist
);
1738 Statusbar::printf("Playlist \"%1%\" cleared", playlist
);
1741 bool SortPlaylist::canBeRun()
1743 return myScreen
== myPlaylist
;
1746 void SortPlaylist::run()
1748 mySortPlaylistDialog
->switchTo();
1751 bool ReversePlaylist::canBeRun()
1753 return myScreen
== myPlaylist
;
1756 void ReversePlaylist::run()
1758 myPlaylist
->Reverse();
1761 bool Find::canBeRun()
1763 return myScreen
== myHelp
1764 || myScreen
== myLyrics
1765 # ifdef HAVE_CURL_CURL_H
1766 || myScreen
== myLastfm
1767 # endif // HAVE_CURL_CURL_H
1773 using Global::wFooter
;
1777 Statusbar::ScopedLock slock
;
1778 Statusbar::put() << "Find: ";
1779 token
= wFooter
->prompt();
1782 Statusbar::print("Searching...");
1783 auto s
= static_cast<Screen
<NC::Scrollpad
> *>(myScreen
);
1784 s
->main().removeProperties();
1785 if (token
.empty() || s
->main().setProperties(NC::Format::Reverse
, token
, NC::Format::NoReverse
))
1786 Statusbar::print("Done");
1788 Statusbar::print("No matching patterns found");
1792 bool FindItemBackward::canBeRun()
1794 auto w
= dynamic_cast<Searchable
*>(myScreen
);
1795 return w
&& w
->allowsSearching();
1798 void FindItemForward::run()
1800 findItem(SearchDirection::Forward
);
1801 listsChangeFinisher();
1804 bool FindItemForward::canBeRun()
1806 auto w
= dynamic_cast<Searchable
*>(myScreen
);
1807 return w
&& w
->allowsSearching();
1810 void FindItemBackward::run()
1812 findItem(SearchDirection::Backward
);
1813 listsChangeFinisher();
1816 bool NextFoundItem::canBeRun()
1818 return dynamic_cast<Searchable
*>(myScreen
);
1821 void NextFoundItem::run()
1823 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
1824 assert(w
!= nullptr);
1825 w
->find(SearchDirection::Forward
, Config
.wrapped_search
, true);
1826 listsChangeFinisher();
1829 bool PreviousFoundItem::canBeRun()
1831 return dynamic_cast<Searchable
*>(myScreen
);
1834 void PreviousFoundItem::run()
1836 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
1837 assert(w
!= nullptr);
1838 w
->find(SearchDirection::Backward
, Config
.wrapped_search
, true);
1839 listsChangeFinisher();
1842 void ToggleFindMode::run()
1844 Config
.wrapped_search
= !Config
.wrapped_search
;
1845 Statusbar::printf("Search mode: %1%",
1846 Config
.wrapped_search
? "Wrapped" : "Normal"
1850 void ToggleReplayGainMode::run()
1852 using Global::wFooter
;
1856 Statusbar::ScopedLock slock
;
1857 Statusbar::put() << "Replay gain mode? "
1858 << "[" << NC::Format::Bold
<< 'o' << NC::Format::NoBold
<< "ff"
1859 << "/" << NC::Format::Bold
<< 't' << NC::Format::NoBold
<< "rack"
1860 << "/" << NC::Format::Bold
<< 'a' << NC::Format::NoBold
<< "lbum"
1862 rgm
= Statusbar::Helpers::promptReturnOneOf({"t", "a", "o"})[0];
1867 Mpd
.SetReplayGainMode(MPD::rgmTrack
);
1870 Mpd
.SetReplayGainMode(MPD::rgmAlbum
);
1873 Mpd
.SetReplayGainMode(MPD::rgmOff
);
1875 default: // impossible
1876 throw std::runtime_error(
1877 (boost::format("ToggleReplayGainMode: impossible case reached: %1%") % rgm
).str()
1880 Statusbar::printf("Replay gain mode: %1%", Mpd
.GetReplayGainMode());
1883 void ToggleAddMode::run()
1885 std::string mode_desc
;
1886 switch (Config
.space_add_mode
)
1888 case SpaceAddMode::AddRemove
:
1889 Config
.space_add_mode
= SpaceAddMode::AlwaysAdd
;
1890 mode_desc
= "always add an item to playlist";
1892 case SpaceAddMode::AlwaysAdd
:
1893 Config
.space_add_mode
= SpaceAddMode::AddRemove
;
1894 mode_desc
= "add an item to playlist or remove if already added";
1897 Statusbar::printf("Add mode: %1%", mode_desc
);
1900 void ToggleMouse::run()
1902 Config
.mouse_support
= !Config
.mouse_support
;
1903 if (Config
.mouse_support
)
1904 NC::Mouse::enable();
1906 NC::Mouse::disable();
1907 Statusbar::printf("Mouse support %1%",
1908 Config
.mouse_support
? "enabled" : "disabled"
1912 void ToggleBitrateVisibility::run()
1914 Config
.display_bitrate
= !Config
.display_bitrate
;
1915 Statusbar::printf("Bitrate visibility %1%",
1916 Config
.display_bitrate
? "enabled" : "disabled"
1920 void AddRandomItems::run()
1922 using Global::wFooter
;
1925 Statusbar::ScopedLock slock
;
1926 Statusbar::put() << "Add random? "
1927 << "[" << NC::Format::Bold
<< 's' << NC::Format::NoBold
<< "ongs"
1928 << "/" << NC::Format::Bold
<< 'a' << NC::Format::NoBold
<< "rtists"
1929 << "/" << "album" << NC::Format::Bold
<< 'A' << NC::Format::NoBold
<< "rtists"
1930 << "/" << "al" << NC::Format::Bold
<< 'b' << NC::Format::NoBold
<< "ums"
1932 rnd_type
= Statusbar::Helpers::promptReturnOneOf({"s", "a", "A", "b"})[0];
1935 mpd_tag_type tag_type
= MPD_TAG_ARTIST
;
1936 std::string tag_type_str
;
1937 if (rnd_type
!= 's')
1939 tag_type
= charToTagType(rnd_type
);
1940 tag_type_str
= boost::locale::to_lower(tagTypeToString(tag_type
));
1943 tag_type_str
= "song";
1947 Statusbar::ScopedLock slock
;
1948 Statusbar::put() << "Number of random " << tag_type_str
<< "s: ";
1949 number
= fromString
<unsigned>(wFooter
->prompt());
1951 if (number
&& (rnd_type
== 's' ? Mpd
.AddRandomSongs(number
) : Mpd
.AddRandomTag(tag_type
, number
)))
1953 Statusbar::printf("%1% random %2%%3% added to playlist",
1954 number
, tag_type_str
, number
== 1 ? "" : "s"
1959 bool ToggleBrowserSortMode::canBeRun()
1961 return myScreen
== myBrowser
;
1964 void ToggleBrowserSortMode::run()
1966 switch (Config
.browser_sort_mode
)
1968 case SortMode::Name
:
1969 Config
.browser_sort_mode
= SortMode::ModificationTime
;
1970 Statusbar::print("Sort songs by: modification time");
1972 case SortMode::ModificationTime
:
1973 Config
.browser_sort_mode
= SortMode::CustomFormat
;
1974 Statusbar::print("Sort songs by: custom format");
1976 case SortMode::CustomFormat
:
1977 Config
.browser_sort_mode
= SortMode::NoOp
;
1978 Statusbar::print("Do not sort songs");
1980 case SortMode::NoOp
:
1981 Config
.browser_sort_mode
= SortMode::Name
;
1982 Statusbar::print("Sort songs by: name");
1984 if (Config
.browser_sort_mode
!= SortMode::NoOp
)
1986 size_t sort_offset
= myBrowser
->inRootDirectory() ? 0 : 1;
1987 std::sort(myBrowser
->main().begin()+sort_offset
, myBrowser
->main().end(),
1988 LocaleBasedItemSorting(std::locale(), Config
.ignore_leading_the
, Config
.browser_sort_mode
)
1993 bool ToggleLibraryTagType::canBeRun()
1995 return (myScreen
->isActiveWindow(myLibrary
->Tags
))
1996 || (myLibrary
->Columns() == 2 && myScreen
->isActiveWindow(myLibrary
->Albums
));
1999 void ToggleLibraryTagType::run()
2001 using Global::wFooter
;
2005 Statusbar::ScopedLock slock
;
2006 Statusbar::put() << "Tag type? "
2007 << "[" << NC::Format::Bold
<< 'a' << NC::Format::NoBold
<< "rtist"
2008 << "/" << "album" << NC::Format::Bold
<< 'A' << NC::Format::NoBold
<< "rtist"
2009 << "/" << NC::Format::Bold
<< 'y' << NC::Format::NoBold
<< "ear"
2010 << "/" << NC::Format::Bold
<< 'g' << NC::Format::NoBold
<< "enre"
2011 << "/" << NC::Format::Bold
<< 'c' << NC::Format::NoBold
<< "omposer"
2012 << "/" << NC::Format::Bold
<< 'p' << NC::Format::NoBold
<< "erformer"
2014 tag_type
= Statusbar::Helpers::promptReturnOneOf({"a", "A", "y", "g", "c", "p"})[0];
2016 mpd_tag_type new_tagitem
= charToTagType(tag_type
);
2017 if (new_tagitem
!= Config
.media_lib_primary_tag
)
2019 Config
.media_lib_primary_tag
= new_tagitem
;
2020 std::string item_type
= tagTypeToString(Config
.media_lib_primary_tag
);
2021 myLibrary
->Tags
.setTitle(Config
.titles_visibility
? item_type
+ "s" : "");
2022 myLibrary
->Tags
.reset();
2023 item_type
= boost::locale::to_lower(item_type
);
2024 std::string and_mtime
= Config
.media_library_sort_by_mtime
?
2027 if (myLibrary
->Columns() == 2)
2029 myLibrary
->Songs
.clear();
2030 myLibrary
->Albums
.reset();
2031 myLibrary
->Albums
.clear();
2032 myLibrary
->Albums
.setTitle(Config
.titles_visibility
? "Albums (sorted by " + item_type
+ and_mtime
+ ")" : "");
2033 myLibrary
->Albums
.display();
2037 myLibrary
->Tags
.clear();
2038 myLibrary
->Tags
.display();
2040 Statusbar::printf("Switched to the list of %1%s", item_type
);
2044 bool ToggleMediaLibrarySortMode::canBeRun()
2046 return myScreen
== myLibrary
;
2049 void ToggleMediaLibrarySortMode::run()
2051 myLibrary
->toggleSortMode();
2054 bool RefetchLyrics::canBeRun()
2056 # ifdef HAVE_CURL_CURL_H
2057 return myScreen
== myLyrics
;
2060 # endif // HAVE_CURL_CURL_H
2063 void RefetchLyrics::run()
2065 # ifdef HAVE_CURL_CURL_H
2066 myLyrics
->Refetch();
2067 # endif // HAVE_CURL_CURL_H
2070 bool SetSelectedItemsPriority::canBeRun()
2072 if (Mpd
.Version() < 17)
2074 Statusbar::print("Priorities are supported in MPD >= 0.17.0");
2077 return myScreen
== myPlaylist
&& !myPlaylist
->main().empty();
2080 void SetSelectedItemsPriority::run()
2082 using Global::wFooter
;
2086 Statusbar::ScopedLock slock
;
2087 Statusbar::put() << "Set priority [0-255]: ";
2088 prio
= fromString
<unsigned>(wFooter
->prompt());
2089 boundsCheck(prio
, 0u, 255u);
2091 myPlaylist
->SetSelectedItemsPriority(prio
);
2094 bool SetVisualizerSampleMultiplier::canBeRun()
2096 # ifdef ENABLE_VISUALIZER
2097 return myScreen
== myVisualizer
;
2100 # endif // ENABLE_VISUALIZER
2103 void SetVisualizerSampleMultiplier::run()
2105 # ifdef ENABLE_VISUALIZER
2106 using Global::wFooter
;
2110 Statusbar::ScopedLock slock
;
2111 Statusbar::put() << "Set visualizer sample multiplier: ";
2112 multiplier
= fromString
<double>(wFooter
->prompt());
2113 lowerBoundCheck(multiplier
, 0.0);
2114 Config
.visualizer_sample_multiplier
= multiplier
;
2116 Statusbar::printf("Visualizer sample multiplier set to %1%", multiplier
);
2117 # endif // ENABLE_VISUALIZER
2120 void ShowSongInfo::run()
2122 mySongInfo
->switchTo();
2125 bool ShowArtistInfo::canBeRun()
2127 #ifdef HAVE_CURL_CURL_H
2128 return myScreen
== myLastfm
2129 || (myScreen
->isActiveWindow(myLibrary
->Tags
)
2130 && !myLibrary
->Tags
.empty()
2131 && Config
.media_lib_primary_tag
== MPD_TAG_ARTIST
)
2132 || currentSong(myScreen
);
2135 # endif // NOT HAVE_CURL_CURL_H
2138 void ShowArtistInfo::run()
2140 # ifdef HAVE_CURL_CURL_H
2141 if (myScreen
== myLastfm
)
2143 myLastfm
->switchTo();
2148 if (myScreen
->isActiveWindow(myLibrary
->Tags
))
2150 assert(!myLibrary
->Tags
.empty());
2151 assert(Config
.media_lib_primary_tag
== MPD_TAG_ARTIST
);
2152 artist
= myLibrary
->Tags
.current()->value().tag();
2156 auto s
= currentSong(myScreen
);
2158 artist
= s
->getArtist();
2161 if (!artist
.empty())
2163 myLastfm
->queueJob(LastFm::ArtistInfo(artist
, Config
.lastfm_preferred_language
));
2164 myLastfm
->switchTo();
2166 # endif // HAVE_CURL_CURL_H
2169 void ShowLyrics::run()
2171 myLyrics
->switchTo();
2176 ExitMainLoop
= true;
2179 void NextScreen::run()
2181 if (Config
.screen_switcher_previous
)
2183 if (auto tababble
= dynamic_cast<Tabbable
*>(myScreen
))
2184 tababble
->switchToPreviousScreen();
2186 else if (!Config
.screen_sequence
.empty())
2188 const auto &seq
= Config
.screen_sequence
;
2189 auto screen_type
= std::find(seq
.begin(), seq
.end(), myScreen
->type());
2190 if (++screen_type
== seq
.end())
2191 toScreen(seq
.front())->switchTo();
2193 toScreen(*screen_type
)->switchTo();
2197 void PreviousScreen::run()
2199 if (Config
.screen_switcher_previous
)
2201 if (auto tababble
= dynamic_cast<Tabbable
*>(myScreen
))
2202 tababble
->switchToPreviousScreen();
2204 else if (!Config
.screen_sequence
.empty())
2206 const auto &seq
= Config
.screen_sequence
;
2207 auto screen_type
= std::find(seq
.begin(), seq
.end(), myScreen
->type());
2208 if (screen_type
== seq
.begin())
2209 toScreen(seq
.back())->switchTo();
2211 toScreen(*--screen_type
)->switchTo();
2215 bool ShowHelp::canBeRun()
2217 return myScreen
!= myHelp
2218 # ifdef HAVE_TAGLIB_H
2219 && myScreen
!= myTinyTagEditor
2220 # endif // HAVE_TAGLIB_H
2224 void ShowHelp::run()
2229 bool ShowPlaylist::canBeRun()
2231 return myScreen
!= myPlaylist
2232 # ifdef HAVE_TAGLIB_H
2233 && myScreen
!= myTinyTagEditor
2234 # endif // HAVE_TAGLIB_H
2238 void ShowPlaylist::run()
2240 myPlaylist
->switchTo();
2243 bool ShowBrowser::canBeRun()
2245 return myScreen
!= myBrowser
2246 # ifdef HAVE_TAGLIB_H
2247 && myScreen
!= myTinyTagEditor
2248 # endif // HAVE_TAGLIB_H
2252 void ShowBrowser::run()
2254 myBrowser
->switchTo();
2257 bool ChangeBrowseMode::canBeRun()
2259 return myScreen
== myBrowser
;
2262 void ChangeBrowseMode::run()
2264 myBrowser
->changeBrowseMode();
2267 bool ShowSearchEngine::canBeRun()
2269 return myScreen
!= mySearcher
2270 # ifdef HAVE_TAGLIB_H
2271 && myScreen
!= myTinyTagEditor
2272 # endif // HAVE_TAGLIB_H
2276 void ShowSearchEngine::run()
2278 mySearcher
->switchTo();
2281 bool ResetSearchEngine::canBeRun()
2283 return myScreen
== mySearcher
;
2286 void ResetSearchEngine::run()
2288 mySearcher
->reset();
2291 bool ShowMediaLibrary::canBeRun()
2293 return myScreen
!= myLibrary
2294 # ifdef HAVE_TAGLIB_H
2295 && myScreen
!= myTinyTagEditor
2296 # endif // HAVE_TAGLIB_H
2300 void ShowMediaLibrary::run()
2302 myLibrary
->switchTo();
2305 bool ToggleMediaLibraryColumnsMode::canBeRun()
2307 return myScreen
== myLibrary
;
2310 void ToggleMediaLibraryColumnsMode::run()
2312 myLibrary
->toggleColumnsMode();
2313 myLibrary
->refresh();
2316 bool ShowPlaylistEditor::canBeRun()
2318 return myScreen
!= myPlaylistEditor
2319 # ifdef HAVE_TAGLIB_H
2320 && myScreen
!= myTinyTagEditor
2321 # endif // HAVE_TAGLIB_H
2325 void ShowPlaylistEditor::run()
2327 myPlaylistEditor
->switchTo();
2330 bool ShowTagEditor::canBeRun()
2332 # ifdef HAVE_TAGLIB_H
2333 return myScreen
!= myTagEditor
2334 && myScreen
!= myTinyTagEditor
;
2337 # endif // HAVE_TAGLIB_H
2340 void ShowTagEditor::run()
2342 # ifdef HAVE_TAGLIB_H
2343 if (isMPDMusicDirSet())
2344 myTagEditor
->switchTo();
2345 # endif // HAVE_TAGLIB_H
2348 bool ShowOutputs::canBeRun()
2350 # ifdef ENABLE_OUTPUTS
2351 return myScreen
!= myOutputs
2352 # ifdef HAVE_TAGLIB_H
2353 && myScreen
!= myTinyTagEditor
2354 # endif // HAVE_TAGLIB_H
2358 # endif // ENABLE_OUTPUTS
2361 void ShowOutputs::run()
2363 # ifdef ENABLE_OUTPUTS
2364 myOutputs
->switchTo();
2365 # endif // ENABLE_OUTPUTS
2368 bool ShowVisualizer::canBeRun()
2370 # ifdef ENABLE_VISUALIZER
2371 return myScreen
!= myVisualizer
2372 # ifdef HAVE_TAGLIB_H
2373 && myScreen
!= myTinyTagEditor
2374 # endif // HAVE_TAGLIB_H
2378 # endif // ENABLE_VISUALIZER
2381 void ShowVisualizer::run()
2383 # ifdef ENABLE_VISUALIZER
2384 myVisualizer
->switchTo();
2385 # endif // ENABLE_VISUALIZER
2388 bool ShowClock::canBeRun()
2390 # ifdef ENABLE_CLOCK
2391 return myScreen
!= myClock
2392 # ifdef HAVE_TAGLIB_H
2393 && myScreen
!= myTinyTagEditor
2394 # endif // HAVE_TAGLIB_H
2398 # endif // ENABLE_CLOCK
2401 void ShowClock::run()
2403 # ifdef ENABLE_CLOCK
2404 myClock
->switchTo();
2405 # endif // ENABLE_CLOCK
2408 #ifdef HAVE_TAGLIB_H
2409 bool ShowServerInfo::canBeRun()
2411 return myScreen
!= myTinyTagEditor
;
2413 #endif // HAVE_TAGLIB_H
2415 void ShowServerInfo::run()
2417 myServerInfo
->switchTo();
2424 void populateActions()
2426 auto insert_action
= [](Actions::BaseAction
*a
) {
2427 AvailableActions
[static_cast<size_t>(a
->type())] = a
;
2429 insert_action(new Actions::Dummy());
2430 insert_action(new Actions::MouseEvent());
2431 insert_action(new Actions::ScrollUp());
2432 insert_action(new Actions::ScrollDown());
2433 insert_action(new Actions::ScrollUpArtist());
2434 insert_action(new Actions::ScrollUpAlbum());
2435 insert_action(new Actions::ScrollDownArtist());
2436 insert_action(new Actions::ScrollDownAlbum());
2437 insert_action(new Actions::PageUp());
2438 insert_action(new Actions::PageDown());
2439 insert_action(new Actions::MoveHome());
2440 insert_action(new Actions::MoveEnd());
2441 insert_action(new Actions::ToggleInterface());
2442 insert_action(new Actions::JumpToParentDirectory());
2443 insert_action(new Actions::PressEnter());
2444 insert_action(new Actions::PressSpace());
2445 insert_action(new Actions::SelectItem());
2446 insert_action(new Actions::PreviousColumn());
2447 insert_action(new Actions::NextColumn());
2448 insert_action(new Actions::MasterScreen());
2449 insert_action(new Actions::SlaveScreen());
2450 insert_action(new Actions::VolumeUp());
2451 insert_action(new Actions::VolumeDown());
2452 insert_action(new Actions::DeletePlaylistItems());
2453 insert_action(new Actions::DeleteStoredPlaylist());
2454 insert_action(new Actions::DeleteBrowserItems());
2455 insert_action(new Actions::ReplaySong());
2456 insert_action(new Actions::PreviousSong());
2457 insert_action(new Actions::NextSong());
2458 insert_action(new Actions::Pause());
2459 insert_action(new Actions::Stop());
2460 insert_action(new Actions::ExecuteCommand());
2461 insert_action(new Actions::SavePlaylist());
2462 insert_action(new Actions::MoveSortOrderUp());
2463 insert_action(new Actions::MoveSortOrderDown());
2464 insert_action(new Actions::MoveSelectedItemsUp());
2465 insert_action(new Actions::MoveSelectedItemsDown());
2466 insert_action(new Actions::MoveSelectedItemsTo());
2467 insert_action(new Actions::Add());
2468 insert_action(new Actions::SeekForward());
2469 insert_action(new Actions::SeekBackward());
2470 insert_action(new Actions::ToggleDisplayMode());
2471 insert_action(new Actions::ToggleSeparatorsBetweenAlbums());
2472 insert_action(new Actions::ToggleLyricsFetcher());
2473 insert_action(new Actions::ToggleFetchingLyricsInBackground());
2474 insert_action(new Actions::TogglePlayingSongCentering());
2475 insert_action(new Actions::UpdateDatabase());
2476 insert_action(new Actions::JumpToPlayingSong());
2477 insert_action(new Actions::ToggleRepeat());
2478 insert_action(new Actions::Shuffle());
2479 insert_action(new Actions::ToggleRandom());
2480 insert_action(new Actions::StartSearching());
2481 insert_action(new Actions::SaveTagChanges());
2482 insert_action(new Actions::ToggleSingle());
2483 insert_action(new Actions::ToggleConsume());
2484 insert_action(new Actions::ToggleCrossfade());
2485 insert_action(new Actions::SetCrossfade());
2486 insert_action(new Actions::SetVolume());
2487 insert_action(new Actions::EditSong());
2488 insert_action(new Actions::EditLibraryTag());
2489 insert_action(new Actions::EditLibraryAlbum());
2490 insert_action(new Actions::EditDirectoryName());
2491 insert_action(new Actions::EditPlaylistName());
2492 insert_action(new Actions::EditLyrics());
2493 insert_action(new Actions::JumpToBrowser());
2494 insert_action(new Actions::JumpToMediaLibrary());
2495 insert_action(new Actions::JumpToPlaylistEditor());
2496 insert_action(new Actions::ToggleScreenLock());
2497 insert_action(new Actions::JumpToTagEditor());
2498 insert_action(new Actions::JumpToPositionInSong());
2499 insert_action(new Actions::ReverseSelection());
2500 insert_action(new Actions::RemoveSelection());
2501 insert_action(new Actions::SelectAlbum());
2502 insert_action(new Actions::AddSelectedItems());
2503 insert_action(new Actions::CropMainPlaylist());
2504 insert_action(new Actions::CropPlaylist());
2505 insert_action(new Actions::ClearMainPlaylist());
2506 insert_action(new Actions::ClearPlaylist());
2507 insert_action(new Actions::SortPlaylist());
2508 insert_action(new Actions::ReversePlaylist());
2509 insert_action(new Actions::Find());
2510 insert_action(new Actions::FindItemForward());
2511 insert_action(new Actions::FindItemBackward());
2512 insert_action(new Actions::NextFoundItem());
2513 insert_action(new Actions::PreviousFoundItem());
2514 insert_action(new Actions::ToggleFindMode());
2515 insert_action(new Actions::ToggleReplayGainMode());
2516 insert_action(new Actions::ToggleAddMode());
2517 insert_action(new Actions::ToggleMouse());
2518 insert_action(new Actions::ToggleBitrateVisibility());
2519 insert_action(new Actions::AddRandomItems());
2520 insert_action(new Actions::ToggleBrowserSortMode());
2521 insert_action(new Actions::ToggleLibraryTagType());
2522 insert_action(new Actions::ToggleMediaLibrarySortMode());
2523 insert_action(new Actions::RefetchLyrics());
2524 insert_action(new Actions::SetSelectedItemsPriority());
2525 insert_action(new Actions::SetVisualizerSampleMultiplier());
2526 insert_action(new Actions::ShowSongInfo());
2527 insert_action(new Actions::ShowArtistInfo());
2528 insert_action(new Actions::ShowLyrics());
2529 insert_action(new Actions::Quit());
2530 insert_action(new Actions::NextScreen());
2531 insert_action(new Actions::PreviousScreen());
2532 insert_action(new Actions::ShowHelp());
2533 insert_action(new Actions::ShowPlaylist());
2534 insert_action(new Actions::ShowBrowser());
2535 insert_action(new Actions::ChangeBrowseMode());
2536 insert_action(new Actions::ShowSearchEngine());
2537 insert_action(new Actions::ResetSearchEngine());
2538 insert_action(new Actions::ShowMediaLibrary());
2539 insert_action(new Actions::ToggleMediaLibraryColumnsMode());
2540 insert_action(new Actions::ShowPlaylistEditor());
2541 insert_action(new Actions::ShowTagEditor());
2542 insert_action(new Actions::ShowOutputs());
2543 insert_action(new Actions::ShowVisualizer());
2544 insert_action(new Actions::ShowClock());
2545 insert_action(new Actions::ShowServerInfo());
2548 bool scrollTagCanBeRun(NC::List
*&list
, SongList
*&songs
)
2550 auto w
= myScreen
->activeWindow();
2551 if (list
!= static_cast<void *>(w
))
2552 list
= dynamic_cast<NC::List
*>(w
);
2553 if (songs
!= static_cast<void *>(w
))
2554 songs
= dynamic_cast<SongList
*>(w
);
2555 return list
!= nullptr && !list
->empty()
2556 && songs
!= nullptr;
2559 void scrollTagUpRun(NC::List
*list
, SongList
*songs
, MPD::Song::GetFunction get
)
2561 const auto front
= songs
->beginS();
2562 auto it
= songs
->currentS();
2563 if (auto *s
= it
->get
<Bit::Song
>())
2565 const std::string tag
= s
->getTags(get
);
2569 s
= it
->get
<Bit::Song
>();
2570 if (s
== nullptr || s
->getTags(get
) != tag
)
2573 list
->highlight(it
-front
);
2577 void scrollTagDownRun(NC::List
*list
, SongList
*songs
, MPD::Song::GetFunction get
)
2579 const auto front
= songs
->beginS(), back
= --songs
->endS();
2580 auto it
= songs
->currentS();
2581 if (auto *s
= it
->get
<Bit::Song
>())
2583 const std::string tag
= s
->getTags(get
);
2587 s
= it
->get
<Bit::Song
>();
2588 if (s
== nullptr || s
->getTags(get
) != tag
)
2591 list
->highlight(it
-front
);
2597 using Global::wHeader
;
2598 using Global::wFooter
;
2599 using Global::Timer
;
2600 using Global::SeekingInProgress
;
2602 if (!Status::State::totalTime())
2604 Statusbar::print("Unknown item length");
2608 Progressbar::ScopedLock progressbar_lock
;
2609 Statusbar::ScopedLock statusbar_lock
;
2611 unsigned songpos
= Status::State::elapsedTime();
2614 int old_timeout
= wFooter
->getTimeout();
2615 wFooter
->setTimeout(500);
2617 auto seekForward
= &Actions::get(Actions::Type::SeekForward
);
2618 auto seekBackward
= &Actions::get(Actions::Type::SeekBackward
);
2620 SeekingInProgress
= true;
2625 unsigned howmuch
= Config
.incremental_seeking
2626 ? (Timer
-t
).total_seconds()/2+Config
.seek_time
2629 NC::Key::Type input
= readKey(*wFooter
);
2630 auto k
= Bindings
.get(input
);
2631 if (k
.first
== k
.second
|| !k
.first
->isSingle()) // no single action?
2633 auto a
= k
.first
->action();
2634 if (a
== seekForward
)
2636 if (songpos
< Status::State::totalTime())
2637 songpos
= std::min(songpos
+ howmuch
, Status::State::totalTime());
2639 else if (a
== seekBackward
)
2643 if (songpos
< howmuch
)
2652 *wFooter
<< NC::Format::Bold
;
2653 std::string tracklength
;
2654 // FIXME: merge this with the code in status.cpp
2655 switch (Config
.design
)
2657 case Design::Classic
:
2659 if (Config
.display_remaining_time
)
2662 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime()-songpos
);
2665 tracklength
+= MPD::Song::ShowTime(songpos
);
2667 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime());
2669 *wFooter
<< NC::XY(wFooter
->getWidth()-tracklength
.length(), 1) << tracklength
;
2671 case Design::Alternative
:
2672 if (Config
.display_remaining_time
)
2675 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime()-songpos
);
2678 tracklength
= MPD::Song::ShowTime(songpos
);
2680 tracklength
+= MPD::Song::ShowTime(Status::State::totalTime());
2681 *wHeader
<< NC::XY(0, 0) << tracklength
<< " ";
2685 *wFooter
<< NC::Format::NoBold
;
2686 Progressbar::draw(songpos
, Status::State::totalTime());
2689 SeekingInProgress
= false;
2690 Mpd
.Seek(Status::State::currentSongPosition(), songpos
);
2692 wFooter
->setTimeout(old_timeout
);
2695 void findItem(const SearchDirection direction
)
2697 using Global::wFooter
;
2699 Searchable
*w
= dynamic_cast<Searchable
*>(myScreen
);
2700 assert(w
!= nullptr);
2701 assert(w
->allowsSearching());
2703 std::string constraint
;
2705 Statusbar::ScopedLock slock
;
2706 NC::Window::ScopedPromptHook
prompt_hook(*wFooter
,
2707 Statusbar::Helpers::FindImmediately(w
, direction
)
2709 Statusbar::put() << (boost::format("Find %1%: ") % direction
).str();
2710 constraint
= wFooter
->prompt();
2715 if (constraint
.empty())
2717 Statusbar::printf("Constraint unset");
2718 w
->clearConstraint();
2722 w
->setSearchConstraint(constraint
);
2723 Statusbar::printf("Using constraint \"%1%\"", constraint
);
2726 catch (boost::bad_expression
&e
)
2728 Statusbar::printf("%1%", e
.what());
2732 void listsChangeFinisher()
2734 if (myScreen
== myLibrary
2735 || myScreen
== myPlaylistEditor
2736 # ifdef HAVE_TAGLIB_H
2737 || myScreen
== myTagEditor
2738 # endif // HAVE_TAGLIB_H
2741 if (myScreen
->activeWindow() == &myLibrary
->Tags
)
2743 myLibrary
->Albums
.clear();
2744 myLibrary
->Albums
.refresh();
2745 myLibrary
->Songs
.clear();
2746 myLibrary
->Songs
.refresh();
2747 myLibrary
->updateTimer();
2749 else if (myScreen
->activeWindow() == &myLibrary
->Albums
)
2751 myLibrary
->Songs
.clear();
2752 myLibrary
->Songs
.refresh();
2753 myLibrary
->updateTimer();
2755 else if (myScreen
->isActiveWindow(myPlaylistEditor
->Playlists
))
2757 myPlaylistEditor
->Content
.clear();
2758 myPlaylistEditor
->Content
.refresh();
2759 myPlaylistEditor
->updateTimer();
2761 # ifdef HAVE_TAGLIB_H
2762 else if (myScreen
->activeWindow() == myTagEditor
->Dirs
)
2764 myTagEditor
->Tags
->clear();
2766 # endif // HAVE_TAGLIB_H