actions: make separators toggle work in all screens
[ncmpcpp.git] / src / actions.cpp
blob4e1fc42d66bf0c4f2156d25ccf3f7279e80b2648
1 /***************************************************************************
2 * Copyright (C) 2008-2012 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
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. *
9 * *
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. *
14 * *
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 ***************************************************************************/
21 #include <cassert>
22 #include <cerrno>
23 #include <cstring>
24 #include <iostream>
26 #include "actions.h"
27 #include "charset.h"
28 #include "config.h"
29 #include "display.h"
30 #include "global.h"
31 #include "mpdpp.h"
32 #include "helpers.h"
33 #include "utility/comparators.h"
35 #include "browser.h"
36 #include "clock.h"
37 #include "help.h"
38 #include "keys.h"
39 #include "media_library.h"
40 #include "lastfm.h"
41 #include "lyrics.h"
42 #include "playlist.h"
43 #include "playlist_editor.h"
44 #include "search_engine.h"
45 #include "sel_items_adder.h"
46 #include "server_info.h"
47 #include "song_info.h"
48 #include "outputs.h"
49 #include "utility/string.h"
50 #include "utility/type_conversions.h"
51 #include "tag_editor.h"
52 #include "tiny_tag_editor.h"
53 #include "visualizer.h"
55 #ifdef HAVE_TAGLIB_H
56 # include "fileref.h"
57 # include "tag.h"
58 #endif // HAVE_TAGLIB_H
60 using namespace std::placeholders;
61 using Global::myScreen;
63 bool Action::OriginalStatusbarVisibility;
64 bool Action::DesignChanged;
65 bool Action::OrderResize;
66 bool Action::ExitMainLoop = false;
68 size_t Action::HeaderHeight;
69 size_t Action::FooterHeight;
70 size_t Action::FooterStartY;
72 namespace {//
74 std::map<ActionType, Action *> Actions;
76 void insertAction(Action *a);
77 void populateActions();
81 void Action::ValidateScreenSize()
83 using Global::MainHeight;
85 if (COLS < 20 || MainHeight < 3)
87 NC::destroyScreen();
88 std::cout << "Screen is too small!\n";
89 exit(1);
93 void Action::SetResizeFlags()
95 myHelp->hasToBeResized = 1;
96 myPlaylist->hasToBeResized = 1;
97 myBrowser->hasToBeResized = 1;
98 mySearcher->hasToBeResized = 1;
99 myLibrary->hasToBeResized = 1;
100 myPlaylistEditor->hasToBeResized = 1;
101 myLyrics->hasToBeResized = 1;
102 mySelectedItemsAdder->hasToBeResized = 1;
103 mySongInfo->hasToBeResized = 1;
105 # ifdef HAVE_CURL_CURL_H
106 myLastfm->hasToBeResized = 1;
107 # endif // HAVE_CURL_CURL_H
109 # ifdef HAVE_TAGLIB_H
110 myTinyTagEditor->hasToBeResized = 1;
111 myTagEditor->hasToBeResized = 1;
112 # endif // HAVE_TAGLIB_H
114 # ifdef ENABLE_VISUALIZER
115 myVisualizer->hasToBeResized = 1;
116 # endif // ENABLE_VISUALIZER
118 # ifdef ENABLE_OUTPUTS
119 myOutputs->hasToBeResized = 1;
120 # endif // ENABLE_OUTPUTS
122 # ifdef ENABLE_CLOCK
123 myClock->hasToBeResized = 1;
124 # endif // ENABLE_CLOCK
127 void Action::ResizeScreen()
129 using Global::MainHeight;
130 using Global::RedrawHeader;
131 using Global::RedrawStatusbar;
132 using Global::wHeader;
133 using Global::wFooter;
135 OrderResize = 0;
137 # if defined(USE_PDCURSES)
138 resize_term(0, 0);
139 # else
140 // update internal screen dimensions
141 if (!DesignChanged)
143 endwin();
144 refresh();
145 // get rid of KEY_RESIZE as it sometimes
146 // corrupts our new cool ReadKey() function
147 // because KEY_RESIZE doesn't come from stdin
148 // and thus select cannot detect it
149 timeout(10);
150 getch();
151 timeout(-1);
153 # endif
155 RedrawHeader = true;
156 MainHeight = LINES-(Config.new_design ? 7 : 4);
158 ValidateScreenSize();
160 if (!Config.header_visibility)
161 MainHeight += 2;
162 if (!Config.statusbar_visibility)
163 MainHeight++;
165 SetResizeFlags();
167 ApplyToVisibleWindows(&BasicScreen::Resize);
169 if (Config.header_visibility || Config.new_design)
170 wHeader->resize(COLS, HeaderHeight);
172 FooterStartY = LINES-(Config.statusbar_visibility ? 2 : 1);
173 wFooter->moveTo(0, FooterStartY);
174 wFooter->resize(COLS, Config.statusbar_visibility ? 2 : 1);
176 ApplyToVisibleWindows(&BasicScreen::Refresh);
177 RedrawStatusbar = true;
178 MPD::StatusChanges changes;
179 if (!Mpd.isPlaying() || DesignChanged)
181 changes.PlayerState = 1;
182 if (DesignChanged)
183 changes.Volume = 1;
185 // Note: routines for drawing separator if alternative user
186 // interface is active and header is hidden are placed in
187 // NcmpcppStatusChanges.StatusFlags
188 changes.StatusFlags = 1; // force status update
189 NcmpcppStatusChanged(&Mpd, changes, 0);
190 if (DesignChanged)
192 RedrawStatusbar = true;
193 NcmpcppStatusChanged(&Mpd, MPD::StatusChanges(), 0);
194 DesignChanged = 0;
195 ShowMessage("User interface: %s", Config.new_design ? "Alternative" : "Classic");
197 wFooter->refresh();
198 refresh();
201 void Action::SetWindowsDimensions()
203 using Global::MainStartY;
204 using Global::MainHeight;
206 MainStartY = Config.new_design ? 5 : 2;
207 MainHeight = LINES-(Config.new_design ? 7 : 4);
209 if (!Config.header_visibility)
211 MainStartY -= 2;
212 MainHeight += 2;
214 if (!Config.statusbar_visibility)
215 MainHeight++;
217 HeaderHeight = Config.new_design ? (Config.header_visibility ? 5 : 3) : 1;
218 FooterStartY = LINES-(Config.statusbar_visibility ? 2 : 1);
219 FooterHeight = Config.statusbar_visibility ? 2 : 1;
222 void Action::Seek()
224 using Global::wHeader;
225 using Global::wFooter;
226 using Global::SeekingInProgress;
228 if (!Mpd.GetTotalTime())
230 ShowMessage("Unknown item length");
231 return;
234 LockProgressbar();
235 LockStatusbar();
237 int songpos = Mpd.GetElapsedTime();
238 time_t t = time(0);
240 int old_timeout = wFooter->getTimeout();
241 wFooter->setTimeout(500);
243 SeekingInProgress = true;
244 while (true)
246 TraceMpdStatus();
247 myPlaylist->UpdateTimer();
249 int howmuch = Config.incremental_seeking ? (myPlaylist->Timer()-t)/2+Config.seek_time : Config.seek_time;
251 Key input = Key::read(*wFooter);
252 auto k = Keys.Bindings.equal_range(input);
253 if (k.first == k.second || !k.first->second.isSingle()) // no single action?
254 break;
255 Action *a = k.first->second.action();
256 if (dynamic_cast<SeekForward *>(a))
258 if (songpos < Mpd.GetTotalTime())
260 songpos += howmuch;
261 if (songpos > Mpd.GetTotalTime())
262 songpos = Mpd.GetTotalTime();
265 else if (dynamic_cast<SeekBackward *>(a))
267 if (songpos > 0)
269 songpos -= howmuch;
270 if (songpos < 0)
271 songpos = 0;
274 else
275 break;
277 *wFooter << NC::fmtBold;
278 std::string tracklength;
279 if (Config.new_design)
281 if (Config.display_remaining_time)
283 tracklength = "-";
284 tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()-songpos);
286 else
287 tracklength = MPD::Song::ShowTime(songpos);
288 tracklength += "/";
289 tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime());
290 *wHeader << NC::XY(0, 0) << tracklength << " ";
291 wHeader->refresh();
293 else
295 tracklength = " [";
296 if (Config.display_remaining_time)
298 tracklength += "-";
299 tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()-songpos);
301 else
302 tracklength += MPD::Song::ShowTime(songpos);
303 tracklength += "/";
304 tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime());
305 tracklength += "]";
306 *wFooter << NC::XY(wFooter->getWidth()-tracklength.length(), 1) << tracklength;
308 *wFooter << NC::fmtBoldEnd;
309 DrawProgressbar(songpos, Mpd.GetTotalTime());
310 wFooter->refresh();
312 SeekingInProgress = false;
313 Mpd.Seek(songpos);
315 wFooter->setTimeout(old_timeout);
317 UnlockProgressbar();
318 UnlockStatusbar();
321 void Action::FindItem(const FindDirection fd)
323 using Global::wFooter;
325 Searchable *w = dynamic_cast<Searchable *>(myScreen);
326 assert(w);
327 assert(w->allowsSearching());
329 LockStatusbar();
330 Statusbar() << "Find " << (fd == fdForward ? "forward" : "backward") << ": ";
331 std::string findme = wFooter->getString();
332 UnlockStatusbar();
334 if (!findme.empty())
335 ShowMessage("Searching...");
337 bool success = w->search(findme);
339 if (findme.empty())
340 return;
342 if (success)
343 ShowMessage("Searching finished");
344 else
345 ShowMessage("Unable to find \"%s\"", findme.c_str());
347 if (fd == fdForward)
348 w->nextFound(Config.wrapped_search);
349 else
350 w->prevFound(Config.wrapped_search);
352 if (myScreen == myPlaylist)
353 myPlaylist->EnableHighlighting();
356 void Action::ListsChangeFinisher()
358 if (myScreen == myLibrary
359 || myScreen == myPlaylistEditor
360 # ifdef HAVE_TAGLIB_H
361 || myScreen == myTagEditor
362 # endif // HAVE_TAGLIB_H
365 if (myScreen->ActiveWindow() == myLibrary->Tags)
367 myLibrary->Albums->clear();
368 myLibrary->Songs->clear();
370 else if (myScreen->ActiveWindow() == myLibrary->Albums)
372 myLibrary->Songs->clear();
374 else if (myScreen->ActiveWindow() == myPlaylistEditor->Playlists)
376 myPlaylistEditor->Content->clear();
378 # ifdef HAVE_TAGLIB_H
379 else if (myScreen->ActiveWindow() == myTagEditor->Dirs)
381 myTagEditor->Tags->clear();
383 # endif // HAVE_TAGLIB_H
387 bool Action::ConnectToMPD()
389 if (!Mpd.Connect())
391 std::cout << "Couldn't connect to MPD ";
392 std::cout << "(host = " << Mpd.GetHostname() << ", port = " << Mpd.GetPort() << ")";
393 std::cout << ": " << Mpd.GetErrorMessage() << std::endl;
394 return false;
396 return true;
399 bool Action::AskYesNoQuestion(const std::string &question, void (*callback)())
401 using Global::wFooter;
403 LockStatusbar();
404 Statusbar() << question << " [" << NC::fmtBold << 'y' << NC::fmtBoldEnd << '/' << NC::fmtBold << 'n' << NC::fmtBoldEnd << "]";
405 wFooter->refresh();
406 int answer = 0;
409 if (callback)
410 callback();
411 answer = wFooter->readKey();
413 while (answer != 'y' && answer != 'n');
414 UnlockStatusbar();
415 return answer == 'y';
418 bool Action::isMPDMusicDirSet()
420 if (Config.mpd_music_dir.empty())
422 ShowMessage("Proper mpd_music_dir variable has to be set in configuration file");
423 return false;
425 return true;
428 Action *Action::Get(ActionType at)
430 if (Actions.empty())
431 populateActions();
432 return Actions[at];
435 Action *Action::Get(const std::string &name)
437 Action *result = 0;
438 if (Actions.empty())
439 populateActions();
440 for (auto it = Actions.begin(); it != Actions.end(); ++it)
442 if (it->second->Name() == name)
444 result = it->second;
445 break;
448 return result;
451 bool MouseEvent::canBeRun() const
453 return Config.mouse_support;
456 void MouseEvent::Run()
458 using Global::VolumeState;
460 itsOldMouseEvent = itsMouseEvent;
461 getmouse(&itsMouseEvent);
462 // workaround shitty ncurses behavior introduced in >=5.8, when we mysteriously get
463 // a few times after ncmpcpp startup 2^27 code instead of BUTTON{1,3}_RELEASED. since that
464 // 2^27 thing shows constantly instead of BUTTON2_PRESSED, it was redefined to be recognized
465 // as BUTTON2_PRESSED. but clearly we don't want to trigger behavior bound to BUTTON2
466 // after BUTTON{1,3} was pressed. so, here is the workaround: if last event was BUTTON{1,3}_PRESSED,
467 // we MUST get BUTTON{1,3}_RELEASED afterwards. if we get BUTTON2_PRESSED, erroneus behavior
468 // is about to occur and we need to prevent that.
469 if (itsOldMouseEvent.bstate & (BUTTON1_PRESSED | BUTTON3_PRESSED) && itsMouseEvent.bstate & BUTTON2_PRESSED)
470 return;
471 if (itsMouseEvent.bstate & BUTTON1_PRESSED
472 && itsMouseEvent.y == LINES-(Config.statusbar_visibility ? 2 : 1)
473 ) // progressbar
475 if (!myPlaylist->isPlaying())
476 return;
477 Mpd.Seek(Mpd.GetTotalTime()*itsMouseEvent.x/double(COLS));
479 else if (itsMouseEvent.bstate & BUTTON1_PRESSED
480 && (Config.statusbar_visibility || Config.new_design)
481 && Mpd.isPlaying()
482 && itsMouseEvent.y == (Config.new_design ? 1 : LINES-1) && itsMouseEvent.x < 9
483 ) // playing/paused
485 Mpd.Toggle();
487 else if ((itsMouseEvent.bstate & BUTTON2_PRESSED || itsMouseEvent.bstate & BUTTON4_PRESSED)
488 && Config.header_visibility
489 && itsMouseEvent.y == 0 && size_t(itsMouseEvent.x) > COLS-VolumeState.length()
490 ) // volume
492 if (itsMouseEvent.bstate & BUTTON2_PRESSED)
493 Mpd.SetVolume(Mpd.GetVolume()-2);
494 else
495 Mpd.SetVolume(Mpd.GetVolume()+2);
497 else if (itsMouseEvent.bstate & (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED))
498 myScreen->MouseButtonPressed(itsMouseEvent);
501 void ScrollUp::Run()
503 myScreen->Scroll(NC::wUp);
504 ListsChangeFinisher();
507 void ScrollDown::Run()
509 myScreen->Scroll(NC::wDown);
510 ListsChangeFinisher();
513 bool ScrollUpArtist::canBeRun() const
515 return proxySongList(myScreen).get();
518 void ScrollUpArtist::Run()
520 auto pl = proxySongList(myScreen);
521 assert(pl);
522 size_t pos = pl->choice();
523 if (MPD::Song *s = pl->getSong(pos))
525 std::string artist = s->getArtist();
526 while (pos > 0)
528 s = pl->getSong(--pos);
529 if (!s || s->getArtist() != artist)
530 break;
532 pl->highlight(pos);
536 bool ScrollUpAlbum::canBeRun() const
538 return proxySongList(myScreen).get();
541 void ScrollUpAlbum::Run()
543 auto pl = proxySongList(myScreen);
544 assert(pl);
545 size_t pos = pl->choice();
546 if (MPD::Song *s = pl->getSong(pos))
548 std::string album = s->getAlbum();
549 while (pos > 0)
551 s = pl->getSong(--pos);
552 if (!s || s->getAlbum() != album)
553 break;
555 pl->highlight(pos);
559 bool ScrollDownArtist::canBeRun() const
561 return proxySongList(myScreen).get();
564 void ScrollDownArtist::Run()
566 auto pl = proxySongList(myScreen);
567 assert(pl);
568 size_t pos = pl->choice();
569 if (MPD::Song *s = pl->getSong(pos))
571 std::string artist = s->getArtist();
572 while (pos < pl->size() - 1)
574 s = pl->getSong(++pos);
575 if (!s || s->getArtist() != artist)
576 break;
578 pl->highlight(pos);
582 bool ScrollDownAlbum::canBeRun() const
584 return proxySongList(myScreen).get();
587 void ScrollDownAlbum::Run()
589 auto pl = proxySongList(myScreen);
590 assert(pl);
591 size_t pos = pl->choice();
592 if (MPD::Song *s = pl->getSong(pos))
594 std::string album = s->getAlbum();
595 while (pos < pl->size() - 1)
597 s = pl->getSong(++pos);
598 if (!s || s->getAlbum() != album)
599 break;
601 pl->highlight(pos);
605 void PageUp::Run()
607 myScreen->Scroll(NC::wPageUp);
608 ListsChangeFinisher();
611 void PageDown::Run()
613 myScreen->Scroll(NC::wPageDown);
614 ListsChangeFinisher();
617 void MoveHome::Run()
619 myScreen->Scroll(NC::wHome);
620 ListsChangeFinisher();
623 void MoveEnd::Run()
625 myScreen->Scroll(NC::wEnd);
626 ListsChangeFinisher();
629 void ToggleInterface::Run()
631 Config.new_design = !Config.new_design;
632 Config.statusbar_visibility = Config.new_design ? 0 : OriginalStatusbarVisibility;
633 SetWindowsDimensions();
634 UnlockProgressbar();
635 UnlockStatusbar();
636 DesignChanged = true;
637 ResizeScreen();
640 bool JumpToParentDir::canBeRun() const
642 return myScreen == myBrowser
643 # ifdef HAVE_TAGLIB_H
644 || myScreen == myTagEditor
645 # endif // HAVE_TAGLIB_H
649 void JumpToParentDir::Run()
651 if (myScreen == myBrowser && myBrowser->CurrentDir() != "/")
653 myBrowser->Main()->reset();
654 myBrowser->EnterPressed();
656 # ifdef HAVE_TAGLIB_H
657 else if (myScreen->ActiveWindow() == myTagEditor->Dirs && myTagEditor->CurrentDir() != "/")
659 myTagEditor->Dirs->reset();
660 myTagEditor->EnterPressed();
662 # endif // HAVE_TAGLIB_H
665 void PressEnter::Run()
667 myScreen->EnterPressed();
670 void PressSpace::Run()
672 myScreen->SpacePressed();
675 bool PreviousColumn::canBeRun() const
677 return (myScreen == myLibrary && myLibrary->isPrevColumnAvailable())
678 || (myScreen == myPlaylistEditor && myPlaylistEditor->isPrevColumnAvailable())
679 # ifdef HAVE_TAGLIB_H
680 || (myScreen == myTagEditor && myTagEditor->isPrevColumnAvailable())
681 # endif // HAVE_TAGLIB_H
685 void PreviousColumn::Run()
687 if (myScreen == myLibrary)
688 myLibrary->PrevColumn();
689 else if (myScreen == myPlaylistEditor)
690 myPlaylistEditor->PrevColumn();
691 # ifdef HAVE_TAGLIB_H
692 else if (myScreen == myTagEditor)
693 myTagEditor->PrevColumn();
694 # endif // HAVE_TAGLIB_H
697 bool NextColumn::canBeRun() const
699 return (myScreen == myLibrary && myLibrary->isNextColumnAvailable())
700 || (myScreen == myPlaylistEditor && myPlaylistEditor->isNextColumnAvailable())
701 # ifdef HAVE_TAGLIB_H
702 || (myScreen == myTagEditor && myTagEditor->isNextColumnAvailable())
703 # endif // HAVE_TAGLIB_H
707 void NextColumn::Run()
709 if (myScreen == myLibrary)
710 myLibrary->NextColumn();
711 else if (myScreen == myPlaylistEditor)
712 myPlaylistEditor->NextColumn();
713 # ifdef HAVE_TAGLIB_H
714 else if (myScreen == myTagEditor)
715 myTagEditor->NextColumn();
716 # endif // HAVE_TAGLIB_H
719 bool MasterScreen::canBeRun() const
721 using Global::myLockedScreen;
722 using Global::myInactiveScreen;
724 return myLockedScreen
725 && myInactiveScreen
726 && myLockedScreen != myScreen
727 && myScreen->isMergable();
730 void MasterScreen::Run()
732 using Global::myInactiveScreen;
733 using Global::myLockedScreen;
734 using Global::RedrawHeader;
736 myInactiveScreen = myScreen;
737 myScreen = myLockedScreen;
738 RedrawHeader = true;
741 bool SlaveScreen::canBeRun() const
743 using Global::myLockedScreen;
744 using Global::myInactiveScreen;
746 return myLockedScreen
747 && myInactiveScreen
748 && myLockedScreen == myScreen
749 && myScreen->isMergable();
752 void SlaveScreen::Run()
754 using Global::myInactiveScreen;
755 using Global::myLockedScreen;
756 using Global::RedrawHeader;
758 myScreen = myInactiveScreen;
759 myInactiveScreen = myLockedScreen;
760 RedrawHeader = true;
763 void VolumeUp::Run()
765 Mpd.SetVolume(Mpd.GetVolume()+1);
768 void VolumeDown::Run()
770 Mpd.SetVolume(Mpd.GetVolume()-1);
773 void Delete::Run()
775 if (myScreen == myPlaylist && !myPlaylist->Items->empty())
777 ShowMessage("Deleting items...");
778 auto delete_fun = std::bind(&MPD::Connection::Delete, _1, _2);
779 if (deleteSelectedSongs(*myPlaylist->Items, delete_fun))
780 ShowMessage("Item(s) deleted");
782 # ifndef WIN32
783 else if (myScreen == myBrowser && !myBrowser->Main()->empty())
785 if (!myBrowser->isLocal() && !isMPDMusicDirSet())
786 return;
788 std::string question;
789 if (myBrowser->Main()->hasSelected())
790 question = "Delete selected items?";
791 else
793 MPD::Item &item = myBrowser->Main()->current().value();
794 std::string name = item.type == MPD::itSong ? item.song->getName() : item.name;
795 question = "Delete ";
796 question += itemTypeToString(item.type);
797 question += " \"";
798 question += Shorten(TO_WSTRING(name), COLS-question.size()-10);
799 question += "\"?";
801 bool yes = AskYesNoQuestion(question, TraceMpdStatus);
802 if (yes)
804 bool success = true;
805 auto list = getSelectedOrCurrent(myBrowser->Main()->begin(), myBrowser->Main()->end(), myBrowser->Main()->currentI());
806 for (auto it = list.begin(); it != list.end(); ++it)
808 const MPD::Item &i = (*it)->value();
809 std::string name = i.type == MPD::itSong ? i.song->getName() : i.name;
810 if (myBrowser->deleteItem(i))
812 const char msg[] = "\"%s\" deleted";
813 ShowMessage(msg, Shorten(TO_WSTRING(name), COLS-const_strlen(msg)).c_str());
815 else
817 const char msg[] = "Couldn't delete \"%s\": %s";
818 ShowMessage(msg, Shorten(TO_WSTRING(name), COLS-const_strlen(msg)-25).c_str(), strerror(errno));
819 success = false;
820 break;
823 if (success)
825 if (!myBrowser->isLocal())
826 Mpd.UpdateDirectory(myBrowser->CurrentDir());
829 else
830 ShowMessage("Aborted");
832 # endif // !WIN32
833 else if (myScreen == myPlaylistEditor && !myPlaylistEditor->Content->empty())
835 if (myScreen->ActiveWindow() == myPlaylistEditor->Playlists)
837 std::string question;
838 if (myPlaylistEditor->Playlists->hasSelected())
839 question = "Delete selected playlists?";
840 else
842 question = "Delete playlist \"";
843 question += Shorten(TO_WSTRING(myPlaylistEditor->Playlists->current().value()), COLS-question.size()-10);
844 question += "\"?";
846 bool yes = AskYesNoQuestion(question, TraceMpdStatus);
847 if (yes)
849 auto list = getSelectedOrCurrent(myPlaylistEditor->Playlists->begin(), myPlaylistEditor->Playlists->end(), myPlaylistEditor->Playlists->currentI());
850 Mpd.StartCommandsList();
851 for (auto it = list.begin(); it != list.end(); ++it)
852 Mpd.DeletePlaylist((*it)->value());
853 if (Mpd.CommitCommandsList())
854 ShowMessage("Playlist%s deleted", list.size() == 1 ? "" : "s");
856 else
857 ShowMessage("Aborted");
859 else if (myScreen->ActiveWindow() == myPlaylistEditor->Content)
861 std::string playlist = myPlaylistEditor->Playlists->current().value();
862 auto delete_fun = std::bind(&MPD::Connection::PlaylistDelete, _1, playlist, _2);
863 ShowMessage("Deleting items...");
864 if (deleteSelectedSongs(*myPlaylistEditor->Content, delete_fun))
865 ShowMessage("Item(s) deleted");
870 void ReplaySong::Run()
872 if (Mpd.isPlaying())
873 Mpd.Seek(0);
876 void PreviousSong::Run()
878 Mpd.Prev();
881 void NextSong::Run()
883 Mpd.Next();
886 void Pause::Run()
888 Mpd.Toggle();
891 void SavePlaylist::Run()
893 using Global::wFooter;
895 LockStatusbar();
896 Statusbar() << "Save playlist as: ";
897 std::string playlist_name = wFooter->getString();
898 std::string real_playlist_name = locale_to_utf_cpy(playlist_name);
899 UnlockStatusbar();
900 if (playlist_name.find("/") != std::string::npos)
902 ShowMessage("Playlist name must not contain slashes");
903 return;
905 if (!playlist_name.empty())
907 if (myPlaylist->Items->isFiltered())
909 Mpd.StartCommandsList();
910 for (size_t i = 0; i < myPlaylist->Items->size(); ++i)
911 Mpd.AddToPlaylist(real_playlist_name, (*myPlaylist->Items)[i].value());
912 Mpd.CommitCommandsList();
913 if (Mpd.GetErrorMessage().empty())
914 ShowMessage("Filtered items added to playlist \"%s\"", playlist_name.c_str());
916 else
918 int result = Mpd.SavePlaylist(real_playlist_name);
919 if (result == MPD_ERROR_SUCCESS)
921 ShowMessage("Playlist saved as \"%s\"", playlist_name.c_str());
922 if (myPlaylistEditor->Main()) // check if initialized
923 myPlaylistEditor->Playlists->clear(); // make playlist's list update itself
925 else if (result == MPD_SERVER_ERROR_EXIST)
927 bool yes = AskYesNoQuestion("Playlist \"" + playlist_name + "\" already exists, overwrite?", TraceMpdStatus);
928 if (yes)
930 Mpd.DeletePlaylist(real_playlist_name);
931 if (Mpd.SavePlaylist(real_playlist_name) == MPD_ERROR_SUCCESS)
932 ShowMessage("Playlist overwritten");
934 else
935 ShowMessage("Aborted");
936 if (myPlaylistEditor->Main()) // check if initialized
937 myPlaylistEditor->Playlists->clear(); // make playlist's list update itself
938 if (myScreen == myPlaylist)
939 myPlaylist->EnableHighlighting();
943 if (myBrowser->Main()
944 && !myBrowser->isLocal()
945 && myBrowser->CurrentDir() == "/"
946 && !myBrowser->Main()->empty())
947 myBrowser->GetDirectory(myBrowser->CurrentDir());
950 void Stop::Run()
952 Mpd.Stop();
955 bool MoveSortOrderUp::canBeRun() const
957 return myScreen == myPlaylist
958 && myPlaylist->SortingInProgress();
961 void MoveSortOrderUp::Run()
963 myPlaylist->moveSortOrderUp();
966 bool MoveSortOrderDown::canBeRun() const
968 return myScreen == myPlaylist
969 && myPlaylist->SortingInProgress();
972 void MoveSortOrderDown::Run()
974 myPlaylist->moveSortOrderDown();
977 bool MoveSelectedItemsUp::canBeRun() const
979 return ((myScreen->ActiveWindow() == myPlaylist->Items
980 && !myPlaylist->Items->empty()
981 && !myPlaylist->Items->isFiltered())
982 || (myScreen->ActiveWindow() == myPlaylistEditor->Content
983 && !myPlaylistEditor->Content->empty()
984 && !myPlaylistEditor->Content->isFiltered()));
987 void MoveSelectedItemsUp::Run()
989 if (myScreen == myPlaylist)
991 moveSelectedItemsUp(*myPlaylist->Items, std::bind(&MPD::Connection::Move, _1, _2, _3));
993 else if (myScreen == myPlaylistEditor)
995 assert(!myPlaylistEditor->Playlists->empty());
996 std::string playlist = myPlaylistEditor->Playlists->current().value();
997 auto move_fun = std::bind(&MPD::Connection::PlaylistMove, _1, playlist, _2, _3);
998 moveSelectedItemsUp(*myPlaylistEditor->Content, move_fun);
1002 bool MoveSelectedItemsDown::canBeRun() const
1004 return ((myScreen->ActiveWindow() == myPlaylist->Items
1005 && !myPlaylist->Items->empty()
1006 && !myPlaylist->Items->isFiltered())
1007 || (myScreen->ActiveWindow() == myPlaylistEditor->Content
1008 && !myPlaylistEditor->Content->empty()
1009 && !myPlaylistEditor->Content->isFiltered()));
1012 void MoveSelectedItemsDown::Run()
1014 if (myScreen == myPlaylist)
1016 moveSelectedItemsDown(*myPlaylist->Items, std::bind(&MPD::Connection::Move, _1, _2, _3));
1018 else if (myScreen == myPlaylistEditor)
1020 assert(!myPlaylistEditor->Playlists->empty());
1021 std::string playlist = myPlaylistEditor->Playlists->current().value();
1022 auto move_fun = std::bind(&MPD::Connection::PlaylistMove, _1, playlist, _2, _3);
1023 moveSelectedItemsDown(*myPlaylistEditor->Content, move_fun);
1027 bool MoveSelectedItemsTo::canBeRun() const
1029 return myScreen->ActiveWindow() == myPlaylist->Items
1030 || myScreen->ActiveWindow() == myPlaylistEditor->Content;
1033 void MoveSelectedItemsTo::Run()
1035 if (myScreen == myPlaylist)
1036 moveSelectedItemsTo(*myPlaylist->Items, std::bind(&MPD::Connection::Move, _1, _2, _3));
1037 else
1039 assert(!myPlaylistEditor->Playlists->empty());
1040 std::string playlist = myPlaylistEditor->Playlists->current().value();
1041 auto move_fun = std::bind(&MPD::Connection::PlaylistMove, _1, playlist, _2, _3);
1042 moveSelectedItemsTo(*myPlaylistEditor->Content, move_fun);
1046 bool Add::canBeRun() const
1048 return myScreen != myPlaylistEditor
1049 || !myPlaylistEditor->Playlists->empty();
1052 void Add::Run()
1054 using Global::wFooter;
1056 LockStatusbar();
1057 Statusbar() << (myScreen == myPlaylistEditor ? "Add to playlist: " : "Add: ");
1058 std::string path = wFooter->getString();
1059 locale_to_utf(path);
1060 UnlockStatusbar();
1061 if (!path.empty())
1063 Statusbar() << "Adding...";
1064 wFooter->refresh();
1065 if (myScreen == myPlaylistEditor)
1066 Mpd.AddToPlaylist(myPlaylistEditor->Playlists->current().value(), path);
1067 else
1069 const char lastfm_url[] = "lastfm://";
1070 if (path.compare(0, const_strlen(lastfm_url), lastfm_url) == 0
1071 || path.find(".asx", path.length()-4) != std::string::npos
1072 || path.find(".cue", path.length()-4) != std::string::npos
1073 || path.find(".m3u", path.length()-4) != std::string::npos
1074 || path.find(".pls", path.length()-4) != std::string::npos
1075 || path.find(".xspf", path.length()-5) != std::string::npos)
1076 Mpd.LoadPlaylist(path);
1077 else
1078 Mpd.Add(path);
1083 bool SeekForward::canBeRun() const
1085 return myPlaylist->NowPlayingSong();
1088 void SeekForward::Run()
1090 Seek();
1093 bool SeekBackward::canBeRun() const
1095 return myPlaylist->NowPlayingSong();
1098 void SeekBackward::Run()
1100 Seek();
1103 bool ToggleDisplayMode::canBeRun() const
1105 return myScreen == myPlaylist
1106 || myScreen == myBrowser
1107 || myScreen == mySearcher
1108 || myScreen->ActiveWindow() == myPlaylistEditor->Content;
1111 void ToggleDisplayMode::Run()
1113 if (myScreen == myPlaylist)
1115 Config.columns_in_playlist = !Config.columns_in_playlist;
1116 ShowMessage("Playlist display mode: %s", Config.columns_in_playlist ? "Columns" : "Classic");
1118 if (Config.columns_in_playlist)
1120 myPlaylist->Items->setItemDisplayer(std::bind(Display::SongsInColumns, _1, myPlaylist));
1121 if (Config.titles_visibility)
1122 myPlaylist->Items->setTitle(Display::Columns(myPlaylist->Items->getWidth()));
1123 else
1124 myPlaylist->Items->setTitle("");
1126 else
1128 myPlaylist->Items->setItemDisplayer(std::bind(Display::Songs, _1, myPlaylist, Config.song_list_format));
1129 myPlaylist->Items->setTitle("");
1132 else if (myScreen == myBrowser)
1134 Config.columns_in_browser = !Config.columns_in_browser;
1135 ShowMessage("Browser display mode: %s", Config.columns_in_browser ? "Columns" : "Classic");
1136 myBrowser->Main()->setTitle(Config.columns_in_browser && Config.titles_visibility ? Display::Columns(myBrowser->Main()->getWidth()) : "");
1138 else if (myScreen == mySearcher)
1140 Config.columns_in_search_engine = !Config.columns_in_search_engine;
1141 ShowMessage("Search engine display mode: %s", Config.columns_in_search_engine ? "Columns" : "Classic");
1142 if (mySearcher->Main()->size() > SearchEngine::StaticOptions)
1143 mySearcher->Main()->setTitle(Config.columns_in_search_engine && Config.titles_visibility ? Display::Columns(mySearcher->Main()->getWidth()) : "");
1145 else if (myScreen->ActiveWindow() == myPlaylistEditor->Content)
1147 Config.columns_in_playlist_editor = !Config.columns_in_playlist_editor;
1148 ShowMessage("Playlist editor display mode: %s", Config.columns_in_playlist_editor ? "Columns" : "Classic");
1149 if (Config.columns_in_playlist_editor)
1150 myPlaylistEditor->Content->setItemDisplayer(std::bind(Display::SongsInColumns, _1, myPlaylistEditor));
1151 else
1152 myPlaylistEditor->Content->setItemDisplayer(std::bind(Display::Songs, _1, myPlaylistEditor, Config.song_list_format));
1156 bool ToggleSeparatorsBetweenAlbums::canBeRun() const
1158 return true;
1161 void ToggleSeparatorsBetweenAlbums::Run()
1163 Config.playlist_separate_albums = !Config.playlist_separate_albums;
1164 ShowMessage("Separators between albums: %s", Config.playlist_separate_albums ? "On" : "Off");
1167 #ifndef HAVE_CURL_CURL_H
1168 bool ToggleLyricsFetcher::canBeRun() const
1170 return false;
1172 #endif // NOT HAVE_CURL_CURL_H
1174 void ToggleLyricsFetcher::Run()
1176 # ifdef HAVE_CURL_CURL_H
1177 myLyrics->ToggleFetcher();
1178 # endif // HAVE_CURL_CURL_H
1181 #ifndef HAVE_CURL_CURL_H
1182 bool ToggleFetchingLyricsInBackground::canBeRun() const
1184 return false;
1186 #endif // NOT HAVE_CURL_CURL_H
1188 void ToggleFetchingLyricsInBackground::Run()
1190 # ifdef HAVE_CURL_CURL_H
1191 Config.fetch_lyrics_in_background = !Config.fetch_lyrics_in_background;
1192 ShowMessage("Fetching lyrics for playing songs in background: %s", Config.fetch_lyrics_in_background ? "On" : "Off");
1193 # endif // HAVE_CURL_CURL_H
1196 void ToggleAutoCenter::Run()
1198 Config.autocenter_mode = !Config.autocenter_mode;
1199 ShowMessage("Auto center mode: %s", Config.autocenter_mode ? "On" : "Off");
1200 if (Config.autocenter_mode && myPlaylist->isPlaying() && !myPlaylist->Items->isFiltered())
1201 myPlaylist->Items->highlight(myPlaylist->NowPlaying);
1204 void UpdateDatabase::Run()
1206 if (myScreen == myBrowser)
1207 Mpd.UpdateDirectory(locale_to_utf_cpy(myBrowser->CurrentDir()));
1208 # ifdef HAVE_TAGLIB_H
1209 else if (myScreen == myTagEditor)
1210 Mpd.UpdateDirectory(myTagEditor->CurrentDir());
1211 # endif // HAVE_TAGLIB_H
1212 else
1213 Mpd.UpdateDirectory("/");
1216 bool JumpToPlayingSong::canBeRun() const
1218 return (myScreen == myPlaylist || myScreen == myBrowser || myScreen == myLibrary)
1219 && myPlaylist->isPlaying();
1222 void JumpToPlayingSong::Run()
1224 using Global::RedrawHeader;
1226 if (myScreen == myPlaylist)
1228 if (myPlaylist->isFiltered())
1229 return;
1230 assert(myPlaylist->isPlaying());
1231 myPlaylist->Items->highlight(myPlaylist->NowPlaying);
1233 else if (myScreen == myBrowser)
1235 const MPD::Song *s = myPlaylist->NowPlayingSong();
1236 assert(s);
1237 myBrowser->LocateSong(*s);
1238 RedrawHeader = true;
1240 else if (myScreen == myLibrary)
1242 const MPD::Song *s = myPlaylist->NowPlayingSong();
1243 assert(s);
1244 myLibrary->LocateSong(*s);
1248 void ToggleRepeat::Run()
1250 Mpd.SetRepeat(!Mpd.GetRepeat());
1253 void Shuffle::Run()
1255 Mpd.Shuffle();
1258 void ToggleRandom::Run()
1260 Mpd.SetRandom(!Mpd.GetRandom());
1263 bool StartSearching::canBeRun() const
1265 return myScreen == mySearcher;
1268 void StartSearching::Run()
1270 if (mySearcher->Main()->at(0).isInactive())
1271 return;
1272 mySearcher->Main()->highlight(SearchEngine::SearchButton);
1273 mySearcher->Main()->setHighlighting(0);
1274 mySearcher->Main()->refresh();
1275 mySearcher->Main()->setHighlighting(1);
1276 mySearcher->EnterPressed();
1279 bool SaveTagChanges::canBeRun() const
1281 # ifdef HAVE_TAGLIB_H
1282 return myScreen == myTinyTagEditor
1283 || myScreen->ActiveWindow() == myTagEditor->TagTypes;
1284 # else
1285 return false;
1286 # endif // HAVE_TAGLIB_H
1289 void SaveTagChanges::Run()
1291 # ifdef HAVE_TAGLIB_H
1292 if (myScreen == myTinyTagEditor)
1294 myTinyTagEditor->Main()->highlight(myTinyTagEditor->Main()->size()-2); // Save
1295 myTinyTagEditor->EnterPressed();
1297 else if (myScreen->ActiveWindow() == myTagEditor->TagTypes)
1299 myTagEditor->TagTypes->highlight(myTagEditor->TagTypes->size()-1); // Save
1300 myTagEditor->EnterPressed();
1302 # endif // HAVE_TAGLIB_H
1305 void ToggleSingle::Run()
1307 Mpd.SetSingle(!Mpd.GetSingle());
1310 void ToggleConsume::Run()
1312 Mpd.SetConsume(!Mpd.GetConsume());
1315 void ToggleCrossfade::Run()
1317 Mpd.SetCrossfade(Mpd.GetCrossfade() ? 0 : Config.crossfade_time);
1320 void SetCrossfade::Run()
1322 using Global::wFooter;
1324 LockStatusbar();
1325 Statusbar() << "Set crossfade to: ";
1326 std::string crossfade = wFooter->getString(3);
1327 UnlockStatusbar();
1328 int cf = stringToInt(crossfade);
1329 if (cf > 0)
1331 Config.crossfade_time = cf;
1332 Mpd.SetCrossfade(cf);
1336 bool EditSong::canBeRun() const
1338 # ifdef HAVE_TAGLIB_H
1339 return currentSong(myScreen);
1340 # else
1341 return false;
1342 # endif // HAVE_TAGLIB_H
1345 void EditSong::Run()
1347 # ifdef HAVE_TAGLIB_H
1348 if (!isMPDMusicDirSet())
1349 return;
1350 auto s = currentSong(myScreen);
1351 assert(s);
1352 myTinyTagEditor->SetEdited(*s);
1353 myTinyTagEditor->SwitchTo();
1354 # endif // HAVE_TAGLIB_H
1357 bool EditLibraryTag::canBeRun() const
1359 # ifdef HAVE_TAGLIB_H
1360 return myScreen->ActiveWindow() == myLibrary->Tags
1361 && !myLibrary->Tags->empty();
1362 # else
1363 return false;
1364 # endif // HAVE_TAGLIB_H
1367 void EditLibraryTag::Run()
1369 # ifdef HAVE_TAGLIB_H
1370 using Global::wFooter;
1372 if (!isMPDMusicDirSet())
1373 return;
1374 LockStatusbar();
1375 Statusbar() << NC::fmtBold << tagTypeToString(Config.media_lib_primary_tag) << NC::fmtBoldEnd << ": ";
1376 std::string new_tag = wFooter->getString(myLibrary->Tags->current().value());
1377 UnlockStatusbar();
1378 if (!new_tag.empty() && new_tag != myLibrary->Tags->current().value())
1380 ShowMessage("Updating tags...");
1381 Mpd.StartSearch(1);
1382 Mpd.AddSearch(Config.media_lib_primary_tag, locale_to_utf_cpy(myLibrary->Tags->current().value()));
1383 MPD::MutableSong::SetFunction set = tagTypeToSetFunction(Config.media_lib_primary_tag);
1384 assert(set);
1385 bool success = true;
1386 MPD::SongList songs = Mpd.CommitSearchSongs();
1387 for (auto s = songs.begin(); s != songs.end(); ++s)
1389 MPD::MutableSong es = *s;
1390 es.setTag(set, new_tag);
1391 ShowMessage("Updating tags in \"%s\"...", es.getName().c_str());
1392 std::string path = Config.mpd_music_dir + es.getURI();
1393 if (!TagEditor::WriteTags(es))
1395 const char msg[] = "Error while updating tags in \"%s\"";
1396 ShowMessage(msg, Shorten(TO_WSTRING(es.getURI()), COLS-const_strlen(msg)).c_str());
1397 success = false;
1398 break;
1401 if (success)
1403 Mpd.UpdateDirectory(getSharedDirectory(songs.begin(), songs.end()));
1404 ShowMessage("Tags updated successfully");
1407 # endif // HAVE_TAGLIB_H
1410 bool EditLibraryAlbum::canBeRun() const
1412 # ifdef HAVE_TAGLIB_H
1413 return myScreen->ActiveWindow() == myLibrary->Albums
1414 && !myLibrary->Albums->empty();
1415 # else
1416 return false;
1417 # endif // HAVE_TAGLIB_H
1420 void EditLibraryAlbum::Run()
1422 # ifdef HAVE_TAGLIB_H
1423 using Global::wFooter;
1425 if (!isMPDMusicDirSet())
1426 return;
1427 LockStatusbar();
1428 Statusbar() << NC::fmtBold << "Album: " << NC::fmtBoldEnd;
1429 std::string new_album = wFooter->getString(myLibrary->Albums->current().value().Album);
1430 UnlockStatusbar();
1431 if (!new_album.empty() && new_album != myLibrary->Albums->current().value().Album)
1433 bool success = 1;
1434 ShowMessage("Updating tags...");
1435 for (size_t i = 0; i < myLibrary->Songs->size(); ++i)
1437 ShowMessage("Updating tags in \"%s\"...", (*myLibrary->Songs)[i].value().getName().c_str());
1438 std::string path = Config.mpd_music_dir + (*myLibrary->Songs)[i].value().getURI();
1439 TagLib::FileRef f(path.c_str());
1440 if (f.isNull())
1442 const char msg[] = "Error while opening file \"%s\"";
1443 ShowMessage(msg, Shorten(TO_WSTRING((*myLibrary->Songs)[i].value().getURI()), COLS-const_strlen(msg)).c_str());
1444 success = 0;
1445 break;
1447 f.tag()->setAlbum(ToWString(new_album));
1448 if (!f.save())
1450 const char msg[] = "Error while writing tags in \"%s\"";
1451 ShowMessage(msg, Shorten(TO_WSTRING((*myLibrary->Songs)[i].value().getURI()), COLS-const_strlen(msg)).c_str());
1452 success = 0;
1453 break;
1456 if (success)
1458 Mpd.UpdateDirectory(getSharedDirectory(myLibrary->Songs->beginV(), myLibrary->Songs->endV()));
1459 ShowMessage("Tags updated successfully");
1462 # endif // HAVE_TAGLIB_H
1465 bool EditDirectoryName::canBeRun() const
1467 return (myScreen == myBrowser
1468 && !myBrowser->Main()->empty()
1469 && myBrowser->Main()->current().value().type == MPD::itDirectory)
1470 # ifdef HAVE_TAGLIB_H
1471 || (myScreen->ActiveWindow() == myTagEditor->Dirs
1472 && !myTagEditor->Dirs->empty()
1473 && myTagEditor->Dirs->choice() > 0)
1474 # endif // HAVE_TAGLIB_H
1478 void EditDirectoryName::Run()
1480 using Global::wFooter;
1482 if (!isMPDMusicDirSet())
1483 return;
1484 if (myScreen == myBrowser)
1486 std::string old_dir = myBrowser->Main()->current().value().name;
1487 LockStatusbar();
1488 Statusbar() << NC::fmtBold << "Directory: " << NC::fmtBoldEnd;
1489 std::string new_dir = wFooter->getString(old_dir);
1490 UnlockStatusbar();
1491 if (!new_dir.empty() && new_dir != old_dir)
1493 std::string full_old_dir;
1494 if (!myBrowser->isLocal())
1495 full_old_dir += Config.mpd_music_dir;
1496 full_old_dir += locale_to_utf_cpy(old_dir);
1497 std::string full_new_dir;
1498 if (!myBrowser->isLocal())
1499 full_new_dir += Config.mpd_music_dir;
1500 full_new_dir += locale_to_utf_cpy(new_dir);
1501 int rename_result = rename(full_old_dir.c_str(), full_new_dir.c_str());
1502 if (rename_result == 0)
1504 const char msg[] = "Directory renamed to \"%s\"";
1505 ShowMessage(msg, Shorten(TO_WSTRING(new_dir), COLS-const_strlen(msg)).c_str());
1506 if (!myBrowser->isLocal())
1507 Mpd.UpdateDirectory(locale_to_utf_cpy(getSharedDirectory(old_dir, new_dir)));
1508 myBrowser->GetDirectory(myBrowser->CurrentDir());
1510 else
1512 const char msg[] = "Couldn't rename \"%s\": %s";
1513 ShowMessage(msg, Shorten(TO_WSTRING(old_dir), COLS-const_strlen(msg)-25).c_str(), strerror(errno));
1517 # ifdef HAVE_TAGLIB_H
1518 else if (myScreen->ActiveWindow() == myTagEditor->Dirs)
1520 std::string old_dir = myTagEditor->Dirs->current().value().first;
1521 LockStatusbar();
1522 Statusbar() << NC::fmtBold << "Directory: " << NC::fmtBoldEnd;
1523 std::string new_dir = wFooter->getString(old_dir);
1524 UnlockStatusbar();
1525 if (!new_dir.empty() && new_dir != old_dir)
1527 std::string full_old_dir = Config.mpd_music_dir + myTagEditor->CurrentDir() + "/" + locale_to_utf_cpy(old_dir);
1528 std::string full_new_dir = Config.mpd_music_dir + myTagEditor->CurrentDir() + "/" + locale_to_utf_cpy(new_dir);
1529 if (rename(full_old_dir.c_str(), full_new_dir.c_str()) == 0)
1531 const char msg[] = "Directory renamed to \"%s\"";
1532 ShowMessage(msg, Shorten(TO_WSTRING(new_dir), COLS-const_strlen(msg)).c_str());
1533 Mpd.UpdateDirectory(myTagEditor->CurrentDir());
1535 else
1537 const char msg[] = "Couldn't rename \"%s\": %s";
1538 ShowMessage(msg, Shorten(TO_WSTRING(old_dir), COLS-const_strlen(msg)-25).c_str(), strerror(errno));
1542 # endif // HAVE_TAGLIB_H
1545 bool EditPlaylistName::canBeRun() const
1547 return (myScreen->ActiveWindow() == myPlaylistEditor->Playlists
1548 && !myPlaylistEditor->Playlists->empty())
1549 || (myScreen == myBrowser
1550 && !myBrowser->Main()->empty()
1551 && myBrowser->Main()->current().value().type == MPD::itPlaylist);
1554 void EditPlaylistName::Run()
1556 using Global::wFooter;
1558 std::string old_name;
1559 if (myScreen->ActiveWindow() == myPlaylistEditor->Playlists)
1560 old_name = myPlaylistEditor->Playlists->current().value();
1561 else
1562 old_name = myBrowser->Main()->current().value().name;
1563 LockStatusbar();
1564 Statusbar() << NC::fmtBold << "Playlist: " << NC::fmtBoldEnd;
1565 std::string new_name = wFooter->getString(old_name);
1566 UnlockStatusbar();
1567 if (!new_name.empty() && new_name != old_name)
1569 if (Mpd.Rename(locale_to_utf_cpy(old_name), locale_to_utf_cpy(new_name)))
1571 const char msg[] = "Playlist renamed to \"%s\"";
1572 ShowMessage(msg, Shorten(TO_WSTRING(new_name), COLS-const_strlen(msg)).c_str());
1573 if (myBrowser->Main() && !myBrowser->isLocal())
1574 myBrowser->GetDirectory("/");
1575 if (myPlaylistEditor->Main())
1576 myPlaylistEditor->Playlists->clear();
1581 bool EditLyrics::canBeRun() const
1583 return myScreen == myLyrics;
1586 void EditLyrics::Run()
1588 myLyrics->Edit();
1591 bool JumpToBrowser::canBeRun() const
1593 return currentSong(myScreen);
1596 void JumpToBrowser::Run()
1598 auto s = currentSong(myScreen);
1599 assert(s);
1600 myBrowser->LocateSong(*s);
1603 bool JumpToMediaLibrary::canBeRun() const
1605 return currentSong(myScreen);
1608 void JumpToMediaLibrary::Run()
1610 auto s = currentSong(myScreen);
1611 assert(s);
1612 myLibrary->LocateSong(*s);
1615 bool JumpToPlaylistEditor::canBeRun() const
1617 return myScreen == myBrowser
1618 && myBrowser->Main()->current().value().type == MPD::itPlaylist;
1621 void JumpToPlaylistEditor::Run()
1623 myPlaylistEditor->Locate(myBrowser->Main()->current().value().name);
1626 void ToggleScreenLock::Run()
1628 using Global::wFooter;
1629 using Global::myLockedScreen;
1631 if (myLockedScreen != 0)
1633 BasicScreen::Unlock();
1634 Action::SetResizeFlags();
1635 ShowMessage("Screen unlocked");
1637 else
1639 int part = Config.locked_screen_width_part*100;
1640 if (Config.ask_for_locked_screen_width_part)
1642 LockStatusbar();
1643 Statusbar() << "% of the locked screen's width to be reserved (20-80): ";
1644 std::string str_part = wFooter->getString(intTo<std::string>::apply(Config.locked_screen_width_part*100));
1645 UnlockStatusbar();
1646 if (str_part.empty())
1647 return;
1648 part = stringToInt(str_part);
1650 if (part < 20 || part > 80)
1652 ShowMessage("Number is out of range");
1653 return;
1655 Config.locked_screen_width_part = part/100.0;
1656 if (myScreen->Lock())
1657 ShowMessage("Screen locked (with %d%% width)", part);
1658 else
1659 ShowMessage("Current screen can't be locked");
1663 bool JumpToTagEditor::canBeRun() const
1665 # ifdef HAVE_TAGLIB_H
1666 return currentSong(myScreen);
1667 # else
1668 return false;
1669 # endif // HAVE_TAGLIB_H
1672 void JumpToTagEditor::Run()
1674 # ifdef HAVE_TAGLIB_H
1675 if (!isMPDMusicDirSet())
1676 return;
1677 auto s = currentSong(myScreen);
1678 assert(s);
1679 myTagEditor->LocateSong(*s);
1680 # endif // HAVE_TAGLIB_H
1683 bool JumpToPositionInSong::canBeRun() const
1685 return myPlaylist->NowPlayingSong();
1688 void JumpToPositionInSong::Run()
1690 using Global::wFooter;
1692 if (!Mpd.GetTotalTime())
1694 ShowMessage("Unknown item length");
1695 return;
1698 const MPD::Song *s = myPlaylist->NowPlayingSong();
1699 assert(s);
1701 LockStatusbar();
1702 Statusbar() << "Position to go (in %/mm:ss/seconds(s)): ";
1703 std::string position = wFooter->getString();
1704 UnlockStatusbar();
1706 if (position.empty())
1707 return;
1709 int newpos = 0;
1710 if (position.find(':') != std::string::npos) // probably time in mm:ss
1712 newpos = stringToInt(position)*60 + stringToInt(position.substr(position.find(':')+1));
1713 if (newpos >= 0 && newpos <= Mpd.GetTotalTime())
1714 Mpd.Seek(newpos);
1715 else
1716 ShowMessage("Out of bounds, 0:00-%s possible for mm:ss, %s given", s->getLength().c_str(), MPD::Song::ShowTime(newpos).c_str());
1718 else if (position.find('s') != std::string::npos) // probably position in seconds
1720 newpos = stringToInt(position);
1721 if (newpos >= 0 && newpos <= Mpd.GetTotalTime())
1722 Mpd.Seek(newpos);
1723 else
1724 ShowMessage("Out of bounds, 0-%d possible for seconds, %d given", s->getDuration(), newpos);
1726 else
1728 newpos = stringToInt(position);
1729 if (newpos >= 0 && newpos <= 100)
1730 Mpd.Seek(Mpd.GetTotalTime()*newpos/100.0);
1731 else
1732 ShowMessage("Out of bounds, 0-100 possible for %%, %d given", newpos);
1736 bool ReverseSelection::canBeRun() const
1738 auto w = hasSongs(myScreen);
1739 return w && w->allowsSelection();
1742 void ReverseSelection::Run()
1744 auto w = hasSongs(myScreen);
1745 assert(w);
1746 w->reverseSelection();
1747 ShowMessage("Selection reversed");
1750 bool DeselectItems::canBeRun() const
1752 return proxySongList(myScreen).get();
1755 void DeselectItems::Run()
1757 auto pl = proxySongList(myScreen);
1758 assert(pl);
1759 for (size_t i = 0; i < pl->size(); ++i)
1760 pl->setSelected(i, false);
1763 bool SelectAlbum::canBeRun() const
1765 auto w = hasSongs(myScreen);
1766 return w && w->allowsSelection() && w->getProxySongList().get();
1769 void SelectAlbum::Run()
1771 auto pl = proxySongList(myScreen);
1772 assert(pl);
1773 size_t pos = pl->choice();
1774 if (MPD::Song *s = pl->getSong(pos))
1776 std::string album = s->getAlbum();
1777 // select song under cursor
1778 pl->setSelected(pos, true);
1779 // go up
1780 while (pos > 0)
1782 s = pl->getSong(--pos);
1783 if (!s || s->getAlbum() != album)
1784 break;
1785 else
1786 pl->setSelected(pos, true);
1788 // go down
1789 pos = pl->choice();
1790 while (pos < pl->size() - 1)
1792 s = pl->getSong(++pos);
1793 if (!s || s->getAlbum() != album)
1794 break;
1795 else
1796 pl->setSelected(pos, true);
1798 ShowMessage("Album around cursor position selected");
1802 void AddSelectedItems::Run()
1804 mySelectedItemsAdder->SwitchTo();
1807 void CropMainPlaylist::Run()
1809 bool yes = true;
1810 if (Config.ask_before_clearing_main_playlist)
1811 yes = AskYesNoQuestion("Do you really want to crop main playlist?", TraceMpdStatus);
1812 if (yes)
1814 ShowMessage("Cropping playlist...");
1815 if (cropPlaylist(*myPlaylist->Items, std::bind(&MPD::Connection::Delete, _1, _2)))
1816 ShowMessage("Cropping playlist...");
1820 bool CropPlaylist::canBeRun() const
1822 return myScreen == myPlaylistEditor;
1825 void CropPlaylist::Run()
1827 assert(!myPlaylistEditor->Playlists->empty());
1828 std::string playlist = myPlaylistEditor->Playlists->current().value();
1829 bool yes = true;
1830 if (Config.ask_before_clearing_main_playlist)
1831 yes = AskYesNoQuestion("Do you really want to crop playlist \"" + playlist + "\"?", TraceMpdStatus);
1832 if (yes)
1834 auto delete_fun = std::bind(&MPD::Connection::PlaylistDelete, _1, playlist, _2);
1835 ShowMessage("Cropping playlist \"%s\"...", playlist.c_str());
1836 if (cropPlaylist(*myPlaylistEditor->Content, delete_fun))
1837 ShowMessage("Playlist \"%s\" cropped", playlist.c_str());
1841 void ClearMainPlaylist::Run()
1843 bool yes = true;
1844 if (Config.ask_before_clearing_main_playlist)
1845 yes = AskYesNoQuestion("Do you really want to clear main playlist?", TraceMpdStatus);
1846 if (yes)
1848 auto delete_fun = std::bind(&MPD::Connection::Delete, _1, _2);
1849 auto clear_fun = std::bind(&MPD::Connection::ClearMainPlaylist, _1);
1850 ShowMessage("Deleting items...");
1851 if (clearPlaylist(*myPlaylist->Items, delete_fun, clear_fun))
1852 ShowMessage("Items deleted");
1856 bool ClearPlaylist::canBeRun() const
1858 return myScreen == myPlaylistEditor;
1861 void ClearPlaylist::Run()
1863 assert(!myPlaylistEditor->Playlists->empty());
1864 std::string playlist = myPlaylistEditor->Playlists->current().value();
1865 bool yes = true;
1866 if (Config.ask_before_clearing_main_playlist)
1867 yes = AskYesNoQuestion("Do you really want to clear playlist \"" + playlist + "\"?", TraceMpdStatus);
1868 if (yes)
1870 auto delete_fun = std::bind(&MPD::Connection::PlaylistDelete, _1, playlist, _2);
1871 auto clear_fun = std::bind(&MPD::Connection::ClearPlaylist, _1, playlist);
1872 ShowMessage("Deleting items from \"%s\"...", playlist.c_str());
1873 if (clearPlaylist(*myPlaylistEditor->Content, delete_fun, clear_fun))
1874 ShowMessage("Items deleted from \"%s\"", playlist.c_str());
1878 bool SortPlaylist::canBeRun() const
1880 return myScreen == myPlaylist;
1883 void SortPlaylist::Run()
1885 myPlaylist->Sort();
1888 bool ReversePlaylist::canBeRun() const
1890 return myScreen == myPlaylist;
1893 void ReversePlaylist::Run()
1895 myPlaylist->Reverse();
1898 bool ApplyFilter::canBeRun() const
1900 auto w = dynamic_cast<Filterable *>(myScreen);
1901 return w && w->allowsFiltering();
1904 void ApplyFilter::Run()
1906 using Global::RedrawHeader;
1907 using Global::wFooter;
1909 Filterable *f = dynamic_cast<Filterable *>(myScreen);
1910 assert(f);
1911 assert(f->allowsFiltering());
1913 LockStatusbar();
1914 Statusbar() << NC::fmtBold << "Apply filter: " << NC::fmtBoldEnd;
1915 wFooter->setGetStringHelper(std::bind(StatusbarApplyFilterImmediately, f, _1));
1916 wFooter->getString(f->currentFilter());
1917 wFooter->setGetStringHelper(StatusbargetStringHelper);
1918 UnlockStatusbar();
1920 std::string filter = f->currentFilter();
1921 if (filter.empty())
1923 myPlaylist->Items->clearFilterResults();
1924 ShowMessage("Filtering disabled");
1926 else
1927 ShowMessage("Using filter \"%s\"", filter.c_str());
1929 if (myScreen == myPlaylist)
1931 myPlaylist->EnableHighlighting();
1932 Playlist::ReloadTotalLength = true;
1933 RedrawHeader = true;
1935 ListsChangeFinisher();
1938 void DisableFilter::Run()
1940 using Global::wFooter;
1942 ApplyFilter *applyFilter = dynamic_cast<ApplyFilter *>(Get(aApplyFilter));
1943 if (applyFilter && applyFilter->canBeRun())
1945 // delete current filter
1946 wFooter->pushChar(KEY_CTRL_U);
1947 wFooter->pushChar(KEY_ENTER);
1948 applyFilter->Execute();
1952 bool Find::canBeRun() const
1954 return myScreen == myHelp
1955 || myScreen == myLyrics
1956 # ifdef HAVE_CURL_CURL_H
1957 || myScreen == myLastfm
1958 # endif // HAVE_CURL_CURL_H
1962 void Find::Run()
1964 using Global::wFooter;
1966 LockStatusbar();
1967 Statusbar() << "Find: ";
1968 std::string findme = wFooter->getString();
1969 UnlockStatusbar();
1971 ShowMessage("Searching...");
1972 Screen<NC::Scrollpad> *s = static_cast<Screen<NC::Scrollpad> *>(myScreen);
1973 s->Main()->removeFormatting();
1974 ShowMessage("%s", findme.empty() || s->Main()->setFormatting(NC::fmtReverse, TO_WSTRING(findme), NC::fmtReverseEnd, 0) ? "Done!" : "No matching patterns found");
1975 s->Main()->flush();
1978 bool FindItemBackward::canBeRun() const
1980 auto w = dynamic_cast<Searchable *>(myScreen);
1981 return w && w->allowsSearching();
1984 void FindItemForward::Run()
1986 FindItem(fdForward);
1987 ListsChangeFinisher();
1990 bool FindItemForward::canBeRun() const
1992 auto w = dynamic_cast<Searchable *>(myScreen);
1993 return w && w->allowsSearching();
1996 void FindItemBackward::Run()
1998 FindItem(fdBackward);
1999 ListsChangeFinisher();
2002 bool NextFoundItem::canBeRun() const
2004 return dynamic_cast<Searchable *>(myScreen);
2007 void NextFoundItem::Run()
2009 Searchable *w = dynamic_cast<Searchable *>(myScreen);
2010 assert(w);
2011 w->nextFound(Config.wrapped_search);
2012 ListsChangeFinisher();
2015 bool PreviousFoundItem::canBeRun() const
2017 return dynamic_cast<Searchable *>(myScreen);
2020 void PreviousFoundItem::Run()
2022 Searchable *w = dynamic_cast<Searchable *>(myScreen);
2023 assert(w);
2024 w->prevFound(Config.wrapped_search);
2025 ListsChangeFinisher();
2028 void ToggleFindMode::Run()
2030 Config.wrapped_search = !Config.wrapped_search;
2031 ShowMessage("Search mode: %s", Config.wrapped_search ? "Wrapped" : "Normal");
2034 void ToggleReplayGainMode::Run()
2036 using Global::wFooter;
2038 if (Mpd.Version() < 16)
2040 ShowMessage("Replay gain mode control is supported in MPD >= 0.16.0");
2041 return;
2044 LockStatusbar();
2045 Statusbar() << "Replay gain mode? [" << NC::fmtBold << 'o' << NC::fmtBoldEnd << "ff/" << NC::fmtBold << 't' << NC::fmtBoldEnd << "rack/" << NC::fmtBold << 'a' << NC::fmtBoldEnd << "lbum]";
2046 wFooter->refresh();
2047 int answer = 0;
2050 TraceMpdStatus();
2051 answer = wFooter->readKey();
2053 while (answer != 'o' && answer != 't' && answer != 'a');
2054 UnlockStatusbar();
2055 Mpd.SetReplayGainMode(answer == 't' ? MPD::rgmTrack : (answer == 'a' ? MPD::rgmAlbum : MPD::rgmOff));
2056 ShowMessage("Replay gain mode: %s", Mpd.GetReplayGainMode().c_str());
2059 void ToggleSpaceMode::Run()
2061 Config.space_selects = !Config.space_selects;
2062 ShowMessage("Space mode: %s item", Config.space_selects ? "Select" : "Add");
2065 void ToggleAddMode::Run()
2067 Config.ncmpc_like_songs_adding = !Config.ncmpc_like_songs_adding;
2068 ShowMessage("Add mode: %s", Config.ncmpc_like_songs_adding ? "Add item to playlist, remove if already added" : "Always add item to playlist");
2071 void ToggleMouse::Run()
2073 Config.mouse_support = !Config.mouse_support;
2074 mousemask(Config.mouse_support ? ALL_MOUSE_EVENTS : 0, 0);
2075 ShowMessage("Mouse support %s", Config.mouse_support ? "enabled" : "disabled");
2078 void ToggleBitrateVisibility::Run()
2080 Config.display_bitrate = !Config.display_bitrate;
2081 ShowMessage("Bitrate visibility %s", Config.display_bitrate ? "enabled" : "disabled");
2084 void AddRandomItems::Run()
2086 using Global::wFooter;
2088 LockStatusbar();
2089 Statusbar() << "Add random? [" << NC::fmtBold << 's' << NC::fmtBoldEnd << "ongs/" << NC::fmtBold << 'a' << NC::fmtBoldEnd << "rtists/al" << NC::fmtBold << 'b' << NC::fmtBoldEnd << "ums] ";
2090 wFooter->refresh();
2091 int answer = 0;
2094 TraceMpdStatus();
2095 answer = wFooter->readKey();
2097 while (answer != 's' && answer != 'a' && answer != 'b');
2098 UnlockStatusbar();
2100 mpd_tag_type tag_type = charToTagType(answer);
2101 std::string tag_type_str = answer == 's' ? "song" : tagTypeToString(tag_type);
2102 lowercase(tag_type_str);
2104 LockStatusbar();
2105 Statusbar() << "Number of random " << tag_type_str << "s: ";
2106 size_t number = stringToLongInt(wFooter->getString());
2107 UnlockStatusbar();
2108 if (number && (answer == 's' ? Mpd.AddRandomSongs(number) : Mpd.AddRandomTag(tag_type, number)))
2109 ShowMessage("%zu random %s%s added to playlist", number, tag_type_str.c_str(), number == 1 ? "" : "s");
2112 bool ToggleBrowserSortMode::canBeRun() const
2114 return myScreen == myBrowser;
2117 void ToggleBrowserSortMode::Run()
2119 switch (Config.browser_sort_mode)
2121 case smName:
2122 if (!myBrowser->isLocal())
2124 Config.browser_sort_mode = smMTime;
2125 ShowMessage("Sort songs by: Modification time");
2126 break;
2128 // local browser doesn't support sorting by mtime, so we just skip it.
2129 case smMTime:
2130 Config.browser_sort_mode = smCustomFormat;
2131 ShowMessage("Sort songs by: Custom format");
2132 break;
2133 case smCustomFormat:
2134 Config.browser_sort_mode = smName;
2135 ShowMessage("Sort songs by: Name");
2136 break;
2138 std::sort(myBrowser->Main()->beginV()+(myBrowser->CurrentDir() != "/"), myBrowser->Main()->endV(), CaseInsensitiveSorting());
2141 bool ToggleLibraryTagType::canBeRun() const
2143 return (myScreen->ActiveWindow() == myLibrary->Tags)
2144 || (myLibrary->Columns() == 2 && myScreen->ActiveWindow() == myLibrary->Albums);
2147 void ToggleLibraryTagType::Run()
2149 using Global::wFooter;
2151 LockStatusbar();
2152 Statusbar() << "Tag type? [" << NC::fmtBold << 'a' << NC::fmtBoldEnd << "rtist/album" << NC::fmtBold << 'A' << NC::fmtBoldEnd << "rtist/" << NC::fmtBold << 'y' << NC::fmtBoldEnd << "ear/" << NC::fmtBold << 'g' << NC::fmtBoldEnd << "enre/" << NC::fmtBold << 'c' << NC::fmtBoldEnd << "omposer/" << NC::fmtBold << 'p' << NC::fmtBoldEnd << "erformer] ";
2153 wFooter->refresh();
2154 int answer = 0;
2157 TraceMpdStatus();
2158 answer = wFooter->readKey();
2160 while (answer != 'a' && answer != 'A' && answer != 'y' && answer != 'g' && answer != 'c' && answer != 'p');
2161 UnlockStatusbar();
2162 mpd_tag_type new_tagitem = charToTagType(answer);
2163 if (new_tagitem != Config.media_lib_primary_tag)
2165 Config.media_lib_primary_tag = new_tagitem;
2166 std::string item_type = tagTypeToString(Config.media_lib_primary_tag);
2167 myLibrary->Tags->setTitle(Config.titles_visibility ? item_type + "s" : "");
2168 myLibrary->Tags->reset();
2169 lowercase(item_type);
2170 if (myLibrary->Columns() == 2)
2172 myLibrary->Songs->clear();
2173 myLibrary->Albums->reset();
2174 myLibrary->Albums->clear();
2175 myLibrary->Albums->setTitle(Config.titles_visibility ? "Albums (sorted by " + item_type + ")" : "");
2176 myLibrary->Albums->display();
2178 else
2180 myLibrary->Tags->clear();
2181 myLibrary->Tags->display();
2183 ShowMessage("Switched to list of %s tag", item_type.c_str());
2187 bool RefetchLyrics::canBeRun() const
2189 # ifdef HAVE_CURL_CURL_H
2190 return myScreen == myLyrics;
2191 # else
2192 return false;
2193 # endif // HAVE_CURL_CURL_H
2196 void RefetchLyrics::Run()
2198 # ifdef HAVE_CURL_CURL_H
2199 myLyrics->Refetch();
2200 # endif // HAVE_CURL_CURL_H
2203 bool RefetchArtistInfo::canBeRun() const
2205 # ifdef HAVE_CURL_CURL_H
2206 return myScreen == myLastfm;
2207 # else
2208 return false;
2209 # endif // HAVE_CURL_CURL_H
2212 void RefetchArtistInfo::Run()
2214 # ifdef HAVE_CURL_CURL_H
2215 myLastfm->Refetch();
2216 # endif // HAVE_CURL_CURL_H
2219 bool SetSelectedItemsPriority::canBeRun() const
2221 return myScreen->ActiveWindow() == myPlaylist->Items;
2224 void SetSelectedItemsPriority::Run()
2226 using Global::wFooter;
2228 assert(myScreen->ActiveWindow() == myPlaylist->Items);
2229 if (myPlaylist->Items->empty())
2230 return;
2232 if (Mpd.Version() < 17)
2234 ShowMessage("Priorities are supported in MPD >= 0.17.0");
2235 return;
2238 LockStatusbar();
2239 Statusbar() << "Set priority [0-255]: ";
2240 std::string strprio = wFooter->getString();
2241 UnlockStatusbar();
2242 if (!isInteger(strprio.c_str()))
2243 return;
2244 int prio = atoi(strprio.c_str());
2245 if (prio < 0 || prio > 255)
2247 ShowMessage("Number is out of range");
2248 return;
2250 myPlaylist->SetSelectedItemsPriority(prio);
2253 void ShowSongInfo::Run()
2255 mySongInfo->SwitchTo();
2258 bool ShowArtistInfo::canBeRun() const
2260 #ifdef HAVE_CURL_CURL_H
2261 return myScreen == myLastfm
2262 || (myScreen->ActiveWindow() == myLibrary->Tags
2263 && !myLibrary->Tags->empty()
2264 && Config.media_lib_primary_tag == MPD_TAG_ARTIST)
2265 || currentSong(myScreen);
2266 # else
2267 return false;
2268 # endif // NOT HAVE_CURL_CURL_H
2271 void ShowArtistInfo::Run()
2273 # ifdef HAVE_CURL_CURL_H
2274 if (myScreen == myLastfm || myLastfm->isDownloading())
2276 myLastfm->SwitchTo();
2277 return;
2280 std::string artist;
2281 if (myScreen->ActiveWindow() == myLibrary->Tags)
2283 assert(!myLibrary->Tags->empty());
2284 assert(Config.media_lib_primary_tag == MPD_TAG_ARTIST);
2285 artist = myLibrary->Tags->current().value();
2287 else
2289 auto s = currentSong(myScreen);
2290 assert(s);
2291 artist = s->getArtist();
2294 if (!artist.empty() && myLastfm->SetArtistInfoArgs(artist, Config.lastfm_preferred_language))
2295 myLastfm->SwitchTo();
2296 # endif // HAVE_CURL_CURL_H
2299 void ShowLyrics::Run()
2301 myLyrics->SwitchTo();
2304 void Quit::Run()
2306 ExitMainLoop = true;
2309 void NextScreen::Run()
2311 using Global::myOldScreen;
2312 using Global::myPrevScreen;
2314 if (Config.screen_switcher_previous)
2316 if (myScreen->isTabbable())
2317 myPrevScreen->SwitchTo();
2318 else
2319 myOldScreen->SwitchTo();
2321 else if (!Config.screens_seq.empty())
2323 std::list<BasicScreen *>::const_iterator screen = std::find(Config.screens_seq.begin(), Config.screens_seq.end(), myScreen);
2324 if (++screen == Config.screens_seq.end())
2325 Config.screens_seq.front()->SwitchTo();
2326 else
2327 (*screen)->SwitchTo();
2331 void PreviousScreen::Run()
2333 using Global::myOldScreen;
2334 using Global::myPrevScreen;
2336 if (Config.screen_switcher_previous)
2338 if (myScreen->isTabbable())
2339 myPrevScreen->SwitchTo();
2340 else
2341 myOldScreen->SwitchTo();
2343 else if (!Config.screens_seq.empty())
2345 std::list<BasicScreen *>::const_iterator screen = std::find(Config.screens_seq.begin(), Config.screens_seq.end(), myScreen);
2346 if (screen == Config.screens_seq.begin())
2347 Config.screens_seq.back()->SwitchTo();
2348 else
2349 (*--screen)->SwitchTo();
2353 #ifdef HAVE_TAGLIB_H
2354 bool ShowHelp::canBeRun() const
2356 return myScreen != myTinyTagEditor;
2358 #endif // HAVE_TAGLIB_H
2360 void ShowHelp::Run()
2362 myHelp->SwitchTo();
2365 #ifdef HAVE_TAGLIB_H
2366 bool ShowPlaylist::canBeRun() const
2368 return myScreen != myTinyTagEditor;
2370 #endif // HAVE_TAGLIB_H
2372 void ShowPlaylist::Run()
2374 myPlaylist->SwitchTo();
2377 #ifdef HAVE_TAGLIB_H
2378 bool ShowBrowser::canBeRun() const
2380 return myScreen != myTinyTagEditor;
2382 #endif // HAVE_TAGLIB_H
2384 void ShowBrowser::Run()
2386 myBrowser->SwitchTo();
2389 #ifdef HAVE_TAGLIB_H
2390 bool ShowSearchEngine::canBeRun() const
2392 return myScreen != myTinyTagEditor;
2394 #endif // HAVE_TAGLIB_H
2396 void ShowSearchEngine::Run()
2398 mySearcher->SwitchTo();
2401 #ifdef HAVE_TAGLIB_H
2402 bool ShowMediaLibrary::canBeRun() const
2404 return myScreen != myTinyTagEditor;
2406 #endif // HAVE_TAGLIB_H
2408 void ShowMediaLibrary::Run()
2410 myLibrary->SwitchTo();
2413 #ifdef HAVE_TAGLIB_H
2414 bool ShowPlaylistEditor::canBeRun() const
2416 return myScreen != myTinyTagEditor;
2418 #endif // HAVE_TAGLIB_H
2420 void ShowPlaylistEditor::Run()
2422 myPlaylistEditor->SwitchTo();
2425 bool ShowTagEditor::canBeRun() const
2427 # ifdef HAVE_TAGLIB_H
2428 return myScreen != myTinyTagEditor;
2429 # else
2430 return false;
2431 # endif // HAVE_TAGLIB_H
2434 void ShowTagEditor::Run()
2436 # ifdef HAVE_TAGLIB_H
2437 if (isMPDMusicDirSet())
2438 myTagEditor->SwitchTo();
2439 # endif // HAVE_TAGLIB_H
2442 bool ShowOutputs::canBeRun() const
2444 # ifdef ENABLE_OUTPUTS
2445 # ifdef HAVE_TAGLIB_H
2446 return myScreen != myTinyTagEditor;
2447 # else
2448 return true;
2449 # endif // HAVE_TAGLIB_H
2450 # else
2451 return false;
2452 # endif // ENABLE_OUTPUTS
2455 void ShowOutputs::Run()
2457 # ifdef ENABLE_OUTPUTS
2458 myOutputs->SwitchTo();
2459 # endif // ENABLE_OUTPUTS
2462 bool ShowVisualizer::canBeRun() const
2464 # ifdef ENABLE_OUTPUTS
2465 # ifdef HAVE_TAGLIB_H
2466 return myScreen != myTinyTagEditor;
2467 # else
2468 return true;
2469 # endif // HAVE_TAGLIB_H
2470 # else
2471 return false;
2472 # endif // ENABLE_OUTPUTS
2475 void ShowVisualizer::Run()
2477 # ifdef ENABLE_VISUALIZER
2478 myVisualizer->SwitchTo();
2479 # endif // ENABLE_VISUALIZER
2482 bool ShowClock::canBeRun() const
2484 # ifdef ENABLE_CLOCK
2485 # ifdef HAVE_TAGLIB_H
2486 return myScreen != myTinyTagEditor;
2487 # else
2488 return true;
2489 # endif // HAVE_TAGLIB_H
2490 # else
2491 return false;
2492 # endif // ENABLE_CLOCK
2495 void ShowClock::Run()
2497 # ifdef ENABLE_CLOCK
2498 myClock->SwitchTo();
2499 # endif // ENABLE_CLOCK
2502 #ifdef HAVE_TAGLIB_H
2503 bool ShowServerInfo::canBeRun() const
2505 return myScreen != myTinyTagEditor;
2507 #endif // HAVE_TAGLIB_H
2509 void ShowServerInfo::Run()
2511 myServerInfo->SwitchTo();
2514 namespace {//
2516 void insertAction(Action *a)
2518 Actions[a->Type()] = a;
2521 void populateActions()
2523 insertAction(new Dummy());
2524 insertAction(new MouseEvent());
2525 insertAction(new ScrollUp());
2526 insertAction(new ScrollDown());
2527 insertAction(new ScrollUpArtist());
2528 insertAction(new ScrollUpAlbum());
2529 insertAction(new ScrollDownArtist());
2530 insertAction(new ScrollDownAlbum());
2531 insertAction(new PageUp());
2532 insertAction(new PageDown());
2533 insertAction(new MoveHome());
2534 insertAction(new MoveEnd());
2535 insertAction(new ToggleInterface());
2536 insertAction(new JumpToParentDir());
2537 insertAction(new PressEnter());
2538 insertAction(new PressSpace());
2539 insertAction(new PreviousColumn());
2540 insertAction(new NextColumn());
2541 insertAction(new MasterScreen());
2542 insertAction(new SlaveScreen());
2543 insertAction(new VolumeUp());
2544 insertAction(new VolumeDown());
2545 insertAction(new Delete());
2546 insertAction(new ReplaySong());
2547 insertAction(new PreviousSong());
2548 insertAction(new NextSong());
2549 insertAction(new Pause());
2550 insertAction(new Stop());
2551 insertAction(new SavePlaylist());
2552 insertAction(new MoveSortOrderUp());
2553 insertAction(new MoveSortOrderDown());
2554 insertAction(new MoveSelectedItemsUp());
2555 insertAction(new MoveSelectedItemsDown());
2556 insertAction(new MoveSelectedItemsTo());
2557 insertAction(new Add());
2558 insertAction(new SeekForward());
2559 insertAction(new SeekBackward());
2560 insertAction(new ToggleDisplayMode());
2561 insertAction(new ToggleSeparatorsBetweenAlbums());
2562 insertAction(new ToggleLyricsFetcher());
2563 insertAction(new ToggleFetchingLyricsInBackground());
2564 insertAction(new ToggleAutoCenter());
2565 insertAction(new UpdateDatabase());
2566 insertAction(new JumpToPlayingSong());
2567 insertAction(new ToggleRepeat());
2568 insertAction(new Shuffle());
2569 insertAction(new ToggleRandom());
2570 insertAction(new StartSearching());
2571 insertAction(new SaveTagChanges());
2572 insertAction(new ToggleSingle());
2573 insertAction(new ToggleConsume());
2574 insertAction(new ToggleCrossfade());
2575 insertAction(new SetCrossfade());
2576 insertAction(new EditSong());
2577 insertAction(new EditLibraryTag());
2578 insertAction(new EditLibraryAlbum());
2579 insertAction(new EditDirectoryName());
2580 insertAction(new EditPlaylistName());
2581 insertAction(new EditLyrics());
2582 insertAction(new JumpToBrowser());
2583 insertAction(new JumpToMediaLibrary());
2584 insertAction(new JumpToPlaylistEditor());
2585 insertAction(new ToggleScreenLock());
2586 insertAction(new JumpToTagEditor());
2587 insertAction(new JumpToPositionInSong());
2588 insertAction(new ReverseSelection());
2589 insertAction(new DeselectItems());
2590 insertAction(new SelectAlbum());
2591 insertAction(new AddSelectedItems());
2592 insertAction(new CropMainPlaylist());
2593 insertAction(new CropPlaylist());
2594 insertAction(new ClearMainPlaylist());
2595 insertAction(new ClearPlaylist());
2596 insertAction(new SortPlaylist());
2597 insertAction(new ReversePlaylist());
2598 insertAction(new ApplyFilter());
2599 insertAction(new DisableFilter());
2600 insertAction(new Find());
2601 insertAction(new FindItemForward());
2602 insertAction(new FindItemBackward());
2603 insertAction(new NextFoundItem());
2604 insertAction(new PreviousFoundItem());
2605 insertAction(new ToggleFindMode());
2606 insertAction(new ToggleReplayGainMode());
2607 insertAction(new ToggleSpaceMode());
2608 insertAction(new ToggleAddMode());
2609 insertAction(new ToggleMouse());
2610 insertAction(new ToggleBitrateVisibility());
2611 insertAction(new AddRandomItems());
2612 insertAction(new ToggleBrowserSortMode());
2613 insertAction(new ToggleLibraryTagType());
2614 insertAction(new RefetchLyrics());
2615 insertAction(new RefetchArtistInfo());
2616 insertAction(new SetSelectedItemsPriority());
2617 insertAction(new ShowSongInfo());
2618 insertAction(new ShowArtistInfo());
2619 insertAction(new ShowLyrics());
2620 insertAction(new Quit());
2621 insertAction(new NextScreen());
2622 insertAction(new PreviousScreen());
2623 insertAction(new ShowHelp());
2624 insertAction(new ShowPlaylist());
2625 insertAction(new ShowBrowser());
2626 insertAction(new ShowSearchEngine());
2627 insertAction(new ShowMediaLibrary());
2628 insertAction(new ShowPlaylistEditor());
2629 insertAction(new ShowTagEditor());
2630 insertAction(new ShowOutputs());
2631 insertAction(new ShowVisualizer());
2632 insertAction(new ShowClock());
2633 insertAction(new ShowServerInfo());