tag editor: add support for numerating tracks using xx/xx format
[ncmpcpp.git] / src / ncmpcpp.cpp
blob424ca084d770752b6f84faffa5413721bcfc798c
1 /***************************************************************************
2 * Copyright (C) 2008-2009 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 <cerrno>
22 #include <clocale>
23 #include <csignal>
24 #include <cstring>
25 #include <sys/time.h>
27 #include <iostream>
28 #include <fstream>
29 #include <stdexcept>
31 #include "mpdpp.h"
32 #include "ncmpcpp.h"
34 #include "browser.h"
35 #include "charset.h"
36 #include "clock.h"
37 #include "display.h"
38 #include "global.h"
39 #include "help.h"
40 #include "helpers.h"
41 #include "media_library.h"
42 #include "misc.h"
43 #include "server_info.h"
44 #include "lyrics.h"
45 #include "playlist.h"
46 #include "playlist_editor.h"
47 #include "search_engine.h"
48 #include "settings.h"
49 #include "song.h"
50 #include "info.h"
51 #include "outputs.h"
52 #include "status.h"
53 #include "tag_editor.h"
54 #include "tiny_tag_editor.h"
55 #include "visualizer.h"
57 #define CHECK_PLAYLIST_FOR_FILTERING \
58 if (myPlaylist->Items->isFiltered()) \
59 { \
60 ShowMessage("%s", MPD::Message::FunctionDisabledFilteringEnabled); \
61 continue; \
64 #define CHECK_MPD_MUSIC_DIR \
65 if (Config.mpd_music_dir.empty()) \
66 { \
67 ShowMessage("configuration variable mpd_music_dir is not set!"); \
68 continue; \
71 using namespace Global;
72 using namespace MPD;
74 BasicScreen *Global::myScreen;
75 BasicScreen *Global::myOldScreen;
77 Window *Global::wHeader;
78 Window *Global::wFooter;
80 size_t Global::MainStartY;
81 size_t Global::MainHeight;
83 bool Global::BlockItemListUpdate = 0;
85 bool Global::MessagesAllowed = 0;
86 bool Global::SeekingInProgress = 0;
87 bool Global::RedrawHeader = 1;
89 namespace
91 std::ofstream errorlog;
92 std::streambuf *cerr_buffer;
94 bool design_changed = 0;
95 bool order_resize = 0;
96 size_t header_height, footer_start_y, footer_height;
98 void resize_screen()
100 order_resize = 0;
102 # if defined(USE_PDCURSES)
103 resize_term(0, 0);
104 # else
105 // update internal screen dimensions
106 if (!design_changed)
108 endwin();
109 refresh();
110 // get rid of KEY_RESIZE as it sometimes
111 // corrupts our new cool ReadKey() function
112 // because KEY_RESIZE doesn't come from stdin
113 // and thus select cannot detect it
114 timeout(10);
115 getch();
116 timeout(-1);
118 # endif
120 RedrawHeader = 1;
121 MainHeight = LINES-(Config.new_design ? 7 : 4);
123 if (COLS < 20 || MainHeight < 3)
125 DestroyScreen();
126 std::cout << "Screen is too small!\n";
127 exit(1);
130 if (!Config.header_visibility)
131 MainHeight += 2;
132 if (!Config.statusbar_visibility)
133 MainHeight++;
135 myHelp->hasToBeResized = 1;
136 myPlaylist->hasToBeResized = 1;
137 myBrowser->hasToBeResized = 1;
138 mySearcher->hasToBeResized = 1;
139 myLibrary->hasToBeResized = 1;
140 myPlaylistEditor->hasToBeResized = 1;
141 myInfo->hasToBeResized = 1;
142 myLyrics->hasToBeResized = 1;
143 mySelectedItemsAdder->hasToBeResized = 1;
145 # ifdef HAVE_TAGLIB_H
146 myTinyTagEditor->hasToBeResized = 1;
147 myTagEditor->hasToBeResized = 1;
148 # endif // HAVE_TAGLIB_H
150 # ifdef ENABLE_VISUALIZER
151 myVisualizer->hasToBeResized = 1;
152 # endif // ENABLE_VISUALIZER
154 # ifdef ENABLE_OUTPUTS
155 myOutputs->hasToBeResized = 1;
156 # endif // ENABLE_OUTPUTS
158 # ifdef ENABLE_CLOCK
159 myClock->hasToBeResized = 1;
160 # endif // ENABLE_CLOCK
162 myScreen->Resize();
164 if (Config.header_visibility || Config.new_design)
165 wHeader->Resize(COLS, header_height);
167 footer_start_y = LINES-(Config.statusbar_visibility ? 2 : 1);
168 wFooter->MoveTo(0, footer_start_y);
169 wFooter->Resize(COLS, Config.statusbar_visibility ? 2 : 1);
171 myScreen->Refresh();
172 RedrawStatusbar = 1;
173 StatusChanges changes;
174 if (!Mpd.isPlaying() || design_changed)
176 changes.PlayerState = 1;
177 if (design_changed)
178 changes.Volume = 1;
180 // Note: routines for drawing separator if alternative user
181 // interface is active and header is hidden are placed in
182 // NcmpcppStatusChanges.StatusFlags
183 changes.StatusFlags = 1; // force status update
184 NcmpcppStatusChanged(&Mpd, changes, 0);
185 if (design_changed)
187 RedrawStatusbar = 1;
188 NcmpcppStatusChanged(&Mpd, StatusChanges(), 0);
189 design_changed = 0;
190 ShowMessage("User interface: %s", Config.new_design ? "Alternative" : "Classic");
192 wFooter->Refresh();
195 # if !defined(WIN32)
196 void sighandler(int signal)
198 if (signal == SIGPIPE)
200 ShowMessage("Broken pipe signal caught!");
202 else if (signal == SIGWINCH)
204 order_resize = 1;
207 # endif // !WIN32
209 void do_at_exit()
211 // restore old cerr buffer
212 std::cerr.rdbuf(cerr_buffer);
213 errorlog.close();
214 Mpd.Disconnect();
215 # ifndef USE_PDCURSES // destroying screen somehow crashes pdcurses
216 DestroyScreen();
217 # endif // USE_PDCURSES
218 WindowTitle("");
222 int main(int argc, char *argv[])
224 setlocale(LC_ALL, "");
226 CreateConfigDir();
227 DefaultConfiguration(Config);
228 DefaultKeys(Key);
229 ReadConfiguration(Config);
230 ReadKeys(Key);
232 if (getenv("MPD_HOST"))
233 Mpd.SetHostname(getenv("MPD_HOST"));
234 if (getenv("MPD_PORT"))
235 Mpd.SetPort(atoi(getenv("MPD_PORT")));
237 if (Config.mpd_host != "localhost")
238 Mpd.SetHostname(Config.mpd_host);
239 if (Config.mpd_port != 6600)
240 Mpd.SetPort(Config.mpd_port);
242 Mpd.SetTimeout(Config.mpd_connection_timeout);
244 if (argc > 1)
245 ParseArgv(argc, argv);
247 if (!ConnectToMPD())
248 return -1;
250 // always execute these commands, even if ncmpcpp use exit function
251 atexit(do_at_exit);
253 // redirect std::cerr output to ~/.ncmpcpp/error.log file
254 errorlog.open((config_dir + "error.log").c_str(), std::ios::app);
255 cerr_buffer = std::cerr.rdbuf();
256 std::cerr.rdbuf(errorlog.rdbuf());
258 InitScreen("ncmpc++ ver. "VERSION, Config.colors_enabled);
260 bool real_statusbar_visibility = Config.statusbar_visibility;
262 if (Config.new_design)
263 Config.statusbar_visibility = 0;
265 SetWindowsDimensions(header_height, footer_start_y, footer_height);
267 if (Config.header_visibility || Config.new_design)
269 wHeader = new Window(0, 0, COLS, header_height, "", Config.header_color, brNone);
270 wHeader->Display();
273 wFooter = new Window(0, footer_start_y, COLS, footer_height, "", Config.statusbar_color, brNone);
274 wFooter->SetTimeout(ncmpcpp_window_timeout);
275 wFooter->SetGetStringHelper(StatusbarGetStringHelper);
276 wFooter->AddFDCallback(Mpd.GetFD(), StatusbarMPDCallback);
277 wFooter->CreateHistory();
279 myPlaylist->SwitchTo();
280 myPlaylist->UpdateTimer();
282 Mpd.SetStatusUpdater(NcmpcppStatusChanged, 0);
283 Mpd.SetErrorHandler(NcmpcppErrorCallback, 0);
285 // local variables
286 int input = 0;
288 bool main_exit = 0;
289 bool title_allowed = !Config.display_screens_numbers_on_start;
291 std::string screen_title;
293 timeval past = { 0, 0 };
294 // local variables end
296 # ifndef WIN32
297 signal(SIGPIPE, sighandler);
298 signal(SIGWINCH, sighandler);
299 # endif // !WIN32
301 MEVENT mouse_event;
302 mouseinterval(0);
303 if (Config.mouse_support)
304 mousemask(ALL_MOUSE_EVENTS, 0);
306 if (Config.jump_to_now_playing_song_at_start)
308 TraceMpdStatus();
309 if (myPlaylist->isPlaying())
310 myPlaylist->Items->Highlight(myPlaylist->NowPlaying);
313 while (!main_exit)
315 if (!Mpd.Connected())
317 if (!wFooter->FDCallbacksListEmpty())
318 wFooter->ClearFDCallbacksList();
319 ShowMessage("Attempting to reconnect...");
320 if (Mpd.Connect())
322 ShowMessage("Connected to %s!", Mpd.GetHostname().c_str());
323 wFooter->AddFDCallback(Mpd.GetFD(), StatusbarMPDCallback);
324 MessagesAllowed = 0;
325 UpdateStatusImmediately = 1;
326 # ifdef ENABLE_VISUALIZER
327 myVisualizer->ResetFD();
328 if (myScreen == myVisualizer)
329 myVisualizer->SetFD();
330 myVisualizer->FindOutputID();
331 # endif // ENABLE_VISUALIZER
335 TraceMpdStatus();
337 MessagesAllowed = 1;
339 if (order_resize)
340 resize_screen();
342 // header stuff
343 if (((Timer.tv_sec == past.tv_sec && Timer.tv_usec >= past.tv_usec+500000) || Timer.tv_sec > past.tv_sec)
344 && (myScreen == myPlaylist || myScreen == myBrowser || myScreen == myLyrics)
347 RedrawHeader = 1;
348 gettimeofday(&past, 0);
350 if (Config.header_visibility && RedrawHeader)
352 if (title_allowed)
354 if (Config.new_design)
356 std::basic_string<my_char_t> title = myScreen->Title();
357 *wHeader << XY(0, 3) << wclrtoeol;
358 *wHeader << fmtBold << Config.alternative_ui_separator_color;
359 mvwhline(wHeader->Raw(), 2, 0, 0, COLS);
360 mvwhline(wHeader->Raw(), 4, 0, 0, COLS);
361 *wHeader << XY((COLS-Window::Length(title))/2, 3);
362 *wHeader << Config.header_color << title << clEnd;
363 *wHeader << clEnd << fmtBoldEnd;
365 else
366 *wHeader << XY(0, 0) << wclrtoeol << fmtBold << myScreen->Title() << fmtBoldEnd;
368 else
370 *wHeader << XY(0, Config.new_design ? 3 : 0)
371 << fmtBold << char(Key.Help[0]) << fmtBoldEnd << ":Help "
372 << fmtBold << char(Key.Playlist[0]) << fmtBoldEnd << ":Playlist "
373 << fmtBold << char(Key.Browser[0]) << fmtBoldEnd << ":Browse "
374 << fmtBold << char(Key.SearchEngine[0]) << fmtBoldEnd << ":Search "
375 << fmtBold << char(Key.MediaLibrary[0]) << fmtBoldEnd << ":Library "
376 << fmtBold << char(Key.PlaylistEditor[0]) << fmtBoldEnd << ":Playlist editor";
377 # ifdef HAVE_TAGLIB_H
378 *wHeader << " " << fmtBold << char(Key.TagEditor[0]) << fmtBoldEnd << ":Tag editor";
379 # endif // HAVE_TAGLIB_H
380 # ifdef ENABLE_VISUALIZER
381 *wHeader << " " << fmtBold << char(Key.Visualizer[0]) << fmtBoldEnd << ":Music visualizer";
382 # endif // ENABLE_VISUALIZER
383 # ifdef ENABLE_CLOCK
384 *wHeader << " " << fmtBold << char(Key.Clock[0]) << fmtBoldEnd << ":Clock";
385 # endif // ENABLE_CLOCK
386 if (Config.new_design)
388 *wHeader << fmtBold << Config.alternative_ui_separator_color;
389 mvwhline(wHeader->Raw(), 2, 0, 0, COLS);
390 mvwhline(wHeader->Raw(), 4, 0, 0, COLS);
391 *wHeader << clEnd << fmtBoldEnd;
394 if (!Config.new_design)
396 *wHeader << Config.volume_color;
397 *wHeader << XY(wHeader->GetWidth()-VolumeState.length(), 0) << VolumeState;
398 *wHeader << clEnd;
400 wHeader->Refresh();
401 RedrawHeader = 0;
403 // header stuff end
405 if (input != ERR)
406 myScreen->RefreshWindow();
407 wFooter->ReadKey(input);
409 if (input == ERR)
410 continue;
412 if (!title_allowed)
413 RedrawHeader = 1;
414 title_allowed = 1;
416 if (myScreen == myPlaylist)
417 myPlaylist->EnableHighlighting();
418 else if (
419 myScreen == myLibrary
420 || myScreen == myPlaylistEditor
421 # ifdef HAVE_TAGLIB_H
422 || myScreen == myTagEditor
423 # endif // HAVE_TAGLIB_H
426 if (Keypressed(input, Key.Up)
427 || Keypressed(input, Key.Down)
428 || Keypressed(input, Key.PageUp)
429 || Keypressed(input, Key.PageDown)
430 || Keypressed(input, Key.Home)
431 || Keypressed(input, Key.End)
432 || Keypressed(input, Key.ApplyFilter)
433 || Keypressed(input, Key.FindForward)
434 || Keypressed(input, Key.FindBackward)
435 || Keypressed(input, Key.NextFoundPosition)
436 || Keypressed(input, Key.PrevFoundPosition))
438 if (myScreen->ActiveWindow() == myLibrary->Artists)
440 myLibrary->Albums->Clear();
442 else if (myScreen->ActiveWindow() == myLibrary->Albums)
444 myLibrary->Songs->Clear();
446 else if (myScreen->ActiveWindow() == myPlaylistEditor->Playlists)
448 myPlaylistEditor->Content->Clear();
450 # ifdef HAVE_TAGLIB_H
451 else if (myScreen->ActiveWindow() == myTagEditor->LeftColumn)
453 myTagEditor->Tags->Clear();
455 # endif // HAVE_TAGLIB_H
459 // key mapping beginning
461 if (Keypressed(input, Key.Up))
463 myScreen->Scroll(wUp, Key.Up);
465 else if (Keypressed(input, Key.Down))
467 myScreen->Scroll(wDown, Key.Down);
469 else if (Keypressed(input, Key.PageUp))
471 myScreen->Scroll(wPageUp, Key.PageUp);
473 else if (Keypressed(input, Key.PageDown))
475 myScreen->Scroll(wPageDown, Key.PageDown);
477 else if (Keypressed(input, Key.Home))
479 myScreen->Scroll(wHome);
481 else if (Keypressed(input, Key.End))
483 myScreen->Scroll(wEnd);
485 else if (Config.mouse_support && input == KEY_MOUSE)
487 getmouse(&mouse_event);
488 if (mouse_event.bstate & BUTTON1_PRESSED
489 && mouse_event.y == LINES-(Config.statusbar_visibility ? 2 : 1)
490 ) // progressbar
492 if (!myPlaylist->isPlaying())
493 continue;
494 Mpd.Seek(Mpd.GetTotalTime()*mouse_event.x/double(COLS));
495 UpdateStatusImmediately = 1;
497 else if (mouse_event.bstate & BUTTON1_PRESSED
498 && (Config.statusbar_visibility || Config.new_design)
499 && Mpd.isPlaying()
500 && mouse_event.y == (Config.new_design ? 1 : LINES-1) && mouse_event.x < 9
501 ) // playing/paused
503 Mpd.Toggle();
504 UpdateStatusImmediately = 1;
506 else if ((mouse_event.bstate & BUTTON2_PRESSED || mouse_event.bstate & BUTTON4_PRESSED)
507 && Config.header_visibility
508 && mouse_event.y == 0 && size_t(mouse_event.x) > COLS-VolumeState.length()
509 ) // volume
511 if (mouse_event.bstate & BUTTON2_PRESSED)
512 Mpd.SetVolume(Mpd.GetVolume()-2);
513 else
514 Mpd.SetVolume(Mpd.GetVolume()+2);
516 else if (mouse_event.bstate & (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED))
517 myScreen->MouseButtonPressed(mouse_event);
519 if (Keypressed(input, Key.ToggleInterface))
521 Config.new_design = !Config.new_design;
522 Config.statusbar_visibility = Config.new_design ? 0 : real_statusbar_visibility;
523 SetWindowsDimensions(header_height, footer_start_y, footer_height);
524 UnlockProgressbar();
525 UnlockStatusbar();
526 design_changed = 1;
527 resize_screen();
529 else if (Keypressed(input, Key.GoToParentDir))
531 if (myScreen == myBrowser && myBrowser->CurrentDir() != "/")
533 myBrowser->Main()->Reset();
534 myBrowser->EnterPressed();
537 else if (Keypressed(input, Key.Enter))
539 myScreen->EnterPressed();
541 else if (Keypressed(input, Key.Space))
543 myScreen->SpacePressed();
545 else if (Keypressed(input, Key.VolumeUp))
547 if (myScreen == myLibrary && input == Key.VolumeUp[0])
549 myLibrary->NextColumn();
551 else if (myScreen == myPlaylistEditor && input == Key.VolumeUp[0])
553 myPlaylistEditor->NextColumn();
555 # ifdef HAVE_TAGLIB_H
556 else if (myScreen == myTagEditor && input == Key.VolumeUp[0])
558 myTagEditor->NextColumn();
560 # endif // HAVE_TAGLIB_H
561 else
562 Mpd.SetVolume(Mpd.GetVolume()+1);
564 else if (Keypressed(input, Key.VolumeDown))
566 if (myScreen == myLibrary && input == Key.VolumeDown[0])
568 myLibrary->PrevColumn();
570 else if (myScreen == myPlaylistEditor && input == Key.VolumeDown[0])
572 myPlaylistEditor->PrevColumn();
574 # ifdef HAVE_TAGLIB_H
575 else if (myScreen == myTagEditor && input == Key.VolumeDown[0])
577 myTagEditor->PrevColumn();
579 # endif // HAVE_TAGLIB_H
580 else
581 Mpd.SetVolume(Mpd.GetVolume()-1);
583 else if (Keypressed(input, Key.Delete))
585 if (!myPlaylist->Items->Empty() && myScreen == myPlaylist)
587 if (myPlaylist->Items->hasSelected())
589 std::vector<size_t> list;
590 myPlaylist->Items->GetSelected(list);
591 Mpd.StartCommandsList();
592 for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it)
593 Mpd.DeleteID((*myPlaylist->Items)[*it].GetID());
594 if (Mpd.CommitCommandsList())
596 for (size_t i = 0; i < myPlaylist->Items->Size(); ++i)
597 myPlaylist->Items->Select(i, 0);
598 myPlaylist->FixPositions(list.front());
599 ShowMessage("Selected items deleted!");
602 else
604 Playlist::BlockNowPlayingUpdate = 1;
605 wFooter->SetTimeout(50);
606 int del_counter = 0;
607 while (!myPlaylist->Items->Empty() && Keypressed(input, Key.Delete))
609 size_t id = myPlaylist->Items->Choice();
610 TraceMpdStatus();
611 Playlist::BlockUpdate = 1;
612 myPlaylist->UpdateTimer();
613 // needed for keeping proper position of now playing song.
614 if (myPlaylist->NowPlaying > int(myPlaylist->CurrentSong()->GetPosition())-del_counter)
615 --myPlaylist->NowPlaying;
616 if (Mpd.DeleteID(myPlaylist->CurrentSong()->GetID()))
618 myPlaylist->Items->DeleteOption(id);
619 myPlaylist->Items->Refresh();
620 wFooter->ReadKey(input);
621 ++del_counter;
623 else
624 break;
626 myPlaylist->FixPositions(myPlaylist->Items->Choice());
627 wFooter->SetTimeout(ncmpcpp_window_timeout);
628 Playlist::BlockNowPlayingUpdate = 0;
631 else if (
632 (myScreen == myBrowser && !myBrowser->Main()->Empty() && myBrowser->Main()->Current().type == itPlaylist)
633 || (myScreen->ActiveWindow() == myPlaylistEditor->Playlists)
636 std::string name = myScreen == myBrowser ? myBrowser->Main()->Current().name : myPlaylistEditor->Playlists->Current();
637 LockStatusbar();
638 Statusbar() << "Delete playlist \"" << Shorten(TO_WSTRING(name), COLS-28) << "\" ? [" << fmtBold << 'y' << fmtBoldEnd << '/' << fmtBold << 'n' << fmtBoldEnd << "]";
639 wFooter->Refresh();
640 input = 0;
643 TraceMpdStatus();
644 wFooter->ReadKey(input);
646 while (input != 'y' && input != 'n');
647 UnlockStatusbar();
648 if (input == 'y')
650 if (Mpd.DeletePlaylist(locale_to_utf_cpy(name)))
652 static const char msg[] = "Playlist \"%s\" deleted!";
653 ShowMessage(msg, Shorten(TO_WSTRING(name), COLS-static_strlen(msg)).c_str());
654 if (myBrowser->Main() && !myBrowser->isLocal() && myBrowser->CurrentDir() == "/")
655 myBrowser->GetDirectory("/");
658 else
659 ShowMessage("Aborted!");
660 if (myPlaylistEditor->Main()) // check if initialized
661 myPlaylistEditor->Playlists->Clear(); // make playlists list update itself
663 # ifndef WIN32
664 else if (myScreen == myBrowser && !myBrowser->Main()->Empty() && myBrowser->Main()->Current().type != itPlaylist)
666 if (!myBrowser->isLocal())
667 CHECK_MPD_MUSIC_DIR;
669 MPD::Item &item = myBrowser->Main()->Current();
671 if (item.type == itSong && !Config.allow_physical_files_deletion)
673 ShowMessage("Deleting files is disabled by default, see man page for more details");
674 continue;
676 if (item.type == itDirectory && !Config.allow_physical_directories_deletion)
678 ShowMessage("Deleting directories is disabled by default, see man page for more details");
679 continue;
681 if (item.type == itDirectory && item.song) // parent dir
682 continue;
684 std::string name = item.type == itSong ? item.song->GetName() : item.name;
685 LockStatusbar();
686 Statusbar() << "Delete " << (item.type == itSong ? "file" : "directory") << " \"" << Shorten(TO_WSTRING(name), COLS-30) << "\" ? [" << fmtBold << 'y' << fmtBoldEnd << '/' << fmtBold << 'n' << fmtBoldEnd << "] ";
687 wFooter->Refresh();
688 input = 0;
691 TraceMpdStatus();
692 wFooter->ReadKey(input);
694 while (input != 'y' && input != 'n');
695 UnlockStatusbar();
696 if (input == 'y')
698 std::string path;
699 if (!myBrowser->isLocal())
700 path = Config.mpd_music_dir;
701 path += item.type == itSong ? item.song->GetFile() : item.name;
703 if (item.type == itDirectory)
704 myBrowser->ClearDirectory(path);
706 if (remove(path.c_str()) == 0)
708 static const char msg[] = "\"%s\" deleted!";
709 ShowMessage(msg, Shorten(TO_WSTRING(name), COLS-static_strlen(msg)).c_str());
710 if (!myBrowser->isLocal())
711 Mpd.UpdateDirectory(myBrowser->CurrentDir());
712 else
713 myBrowser->GetDirectory(myBrowser->CurrentDir());
715 else
717 static const char msg[] = "Couldn't remove \"%s\": %s";
718 ShowMessage(msg, Shorten(TO_WSTRING(name), COLS-static_strlen(msg)-25).c_str(), strerror(errno));
721 else
722 ShowMessage("Aborted!");
725 # endif // !WIN32
726 else if (myScreen->ActiveWindow() == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty())
728 if (myPlaylistEditor->Content->hasSelected())
730 std::vector<size_t> list;
731 myPlaylistEditor->Content->GetSelected(list);
732 std::string playlist = locale_to_utf_cpy(myPlaylistEditor->Playlists->Current());
733 ShowMessage("Deleting selected items...");
734 Mpd.StartCommandsList();
735 for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it)
737 Mpd.Delete(playlist, *it);
738 myPlaylistEditor->Content->DeleteOption(*it);
740 Mpd.CommitCommandsList();
741 ShowMessage("Selected items deleted from playlist \"%s\"!", myPlaylistEditor->Playlists->Current().c_str());
743 else
745 wFooter->SetTimeout(50);
746 locale_to_utf(myPlaylistEditor->Playlists->Current());
747 while (!myPlaylistEditor->Content->Empty() && Keypressed(input, Key.Delete))
749 TraceMpdStatus();
750 myPlaylist->UpdateTimer();
751 Mpd.Delete(myPlaylistEditor->Playlists->Current(), myPlaylistEditor->Content->Choice());
752 myPlaylistEditor->Content->DeleteOption(myPlaylistEditor->Content->Choice());
753 myPlaylistEditor->Content->Refresh();
754 wFooter->ReadKey(input);
756 utf_to_locale(myPlaylistEditor->Playlists->Current());
757 wFooter->SetTimeout(ncmpcpp_window_timeout);
761 else if (Keypressed(input, Key.Prev))
763 Mpd.Prev();
764 UpdateStatusImmediately = 1;
766 else if (Keypressed(input, Key.Next))
768 Mpd.Next();
769 UpdateStatusImmediately = 1;
771 else if (Keypressed(input, Key.Pause))
773 Mpd.Toggle();
774 UpdateStatusImmediately = 1;
776 else if (Keypressed(input, Key.SavePlaylist))
778 LockStatusbar();
779 Statusbar() << "Save playlist as: ";
780 std::string playlist_name = wFooter->GetString();
781 std::string real_playlist_name = locale_to_utf_cpy(playlist_name);
782 UnlockStatusbar();
783 if (playlist_name.find("/") != std::string::npos)
785 ShowMessage("Playlist name cannot contain slashes!");
786 continue;
788 if (!playlist_name.empty())
790 if (myPlaylist->Items->isFiltered())
792 Mpd.StartCommandsList();
793 for (size_t i = 0; i < myPlaylist->Items->Size(); ++i)
794 Mpd.AddToPlaylist(real_playlist_name, (*myPlaylist->Items)[i]);
795 Mpd.CommitCommandsList();
796 if (Mpd.GetErrorMessage().empty())
797 ShowMessage("Filtered items added to playlist \"%s\"", playlist_name.c_str());
799 else if (Mpd.SavePlaylist(real_playlist_name))
801 ShowMessage("Playlist saved as: %s", playlist_name.c_str());
802 if (myPlaylistEditor->Main()) // check if initialized
803 myPlaylistEditor->Playlists->Clear(); // make playlist's list update itself
805 else
807 LockStatusbar();
808 Statusbar() << "Playlist already exists, overwrite: " << playlist_name << " ? [" << fmtBold << 'y' << fmtBoldEnd << '/' << fmtBold << 'n' << fmtBoldEnd << "] ";
809 wFooter->Refresh();
810 input = 0;
811 while (input != 'y' && input != 'n')
813 TraceMpdStatus();
814 wFooter->ReadKey(input);
816 UnlockStatusbar();
818 if (input == 'y')
820 Mpd.DeletePlaylist(real_playlist_name);
821 if (Mpd.SavePlaylist(real_playlist_name))
822 ShowMessage("Playlist overwritten!");
824 else
825 ShowMessage("Aborted!");
826 if (myPlaylistEditor->Main()) // check if initialized
827 myPlaylistEditor->Playlists->Clear(); // make playlist's list update itself
828 if (myScreen == myPlaylist)
829 myPlaylist->EnableHighlighting();
832 if (myBrowser->Main()
833 && !myBrowser->isLocal()
834 && myBrowser->CurrentDir() == "/"
835 && !myBrowser->Main()->Empty())
836 myBrowser->GetDirectory(myBrowser->CurrentDir());
838 else if (Keypressed(input, Key.Stop))
840 Mpd.Stop();
841 UpdateStatusImmediately = 1;
843 else if (Keypressed(input, Key.MvSongUp))
845 if (myScreen == myPlaylist && myPlaylist->SortingInProgress())
846 myPlaylist->AdjustSortOrder(input);
847 else if (myScreen == myPlaylist && !myPlaylist->Items->Empty())
849 CHECK_PLAYLIST_FOR_FILTERING;
850 wFooter->SetTimeout(50);
851 if (myPlaylist->Items->hasSelected())
853 std::vector<size_t> list;
854 myPlaylist->Items->GetSelected(list);
855 std::vector<size_t> origs(list);
857 // NOTICE: since ncmpcpp only pretends to move the songs until the key is
858 // released, mpd doesn't know about the change while the songs are moved
859 // so wee need to block playlist update for this time and also if one of
860 // the songs being moved is currently playing, now playing update to prevent
861 // mpd from 'updating' and thus showing wrong position
863 bool modify_now_playing = 0;
864 for (std::vector<size_t>::iterator it = list.begin(); it != list.end(); ++it)
866 if (*it == size_t(myPlaylist->NowPlaying) && list.front() > 0)
868 modify_now_playing = 1;
869 Playlist::BlockNowPlayingUpdate = 1;
870 break;
874 while (Keypressed(input, Key.MvSongUp) && list.front() > 0)
876 TraceMpdStatus();
877 Playlist::BlockUpdate = 1;
878 myPlaylist->UpdateTimer();
879 if (modify_now_playing)
880 --myPlaylist->NowPlaying;
881 for (std::vector<size_t>::iterator it = list.begin(); it != list.end(); ++it)
883 --*it;
884 myPlaylist->Items->at((*it)+1).SetPosition(*it);
885 myPlaylist->Items->at(*it).SetPosition((*it)+1);
886 myPlaylist->Items->Swap(*it, (*it)+1);
888 myPlaylist->Items->Highlight(list[(list.size()-1)/2]);
889 myPlaylist->Items->Refresh();
890 wFooter->ReadKey(input);
892 Playlist::BlockNowPlayingUpdate = 0;
893 Mpd.StartCommandsList();
894 for (size_t i = 0; i < list.size(); ++i)
895 Mpd.Move(origs[i], list[i]);
896 Mpd.CommitCommandsList();
898 else
900 size_t from, to;
901 from = to = myPlaylist->Items->Choice();
902 bool modify_now_playing = from == size_t(myPlaylist->NowPlaying);
903 if (modify_now_playing)
904 Playlist::BlockNowPlayingUpdate = 1;
905 while (Keypressed(input, Key.MvSongUp) && to > 0)
907 TraceMpdStatus();
908 Playlist::BlockUpdate = 1;
909 myPlaylist->UpdateTimer();
910 if (modify_now_playing)
911 --myPlaylist->NowPlaying;
912 --to;
913 myPlaylist->Items->at(from).SetPosition(to);
914 myPlaylist->Items->at(to).SetPosition(from);
915 myPlaylist->Items->Swap(to, to+1);
916 myPlaylist->Items->Scroll(wUp);
917 myPlaylist->Items->Refresh();
918 wFooter->ReadKey(input);
920 Mpd.Move(from, to);
921 Playlist::BlockNowPlayingUpdate = 0;
922 UpdateStatusImmediately = 1;
924 wFooter->SetTimeout(ncmpcpp_window_timeout);
926 else if (myScreen->ActiveWindow() == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty())
928 wFooter->SetTimeout(50);
929 if (myPlaylistEditor->Content->hasSelected())
931 std::vector<size_t> list;
932 myPlaylistEditor->Content->GetSelected(list);
934 std::vector<size_t> origs(list);
936 while (Keypressed(input, Key.MvSongUp) && list.front() > 0)
938 TraceMpdStatus();
939 myPlaylist->UpdateTimer();
940 for (std::vector<size_t>::iterator it = list.begin(); it != list.end(); ++it)
942 --*it;
943 myPlaylistEditor->Content->Swap(*it, (*it)+1);
945 myPlaylistEditor->Content->Highlight(list[(list.size()-1)/2]);
946 myPlaylistEditor->Content->Refresh();
947 wFooter->ReadKey(input);
949 Mpd.StartCommandsList();
950 for (size_t i = 0; i < list.size(); ++i)
951 if (origs[i] != list[i])
952 Mpd.Move(myPlaylistEditor->Playlists->Current(), origs[i], list[i]);
953 Mpd.CommitCommandsList();
955 else
957 size_t from, to;
958 from = to = myPlaylistEditor->Content->Choice();
959 while (Keypressed(input, Key.MvSongUp) && to > 0)
961 TraceMpdStatus();
962 myPlaylist->UpdateTimer();
963 --to;
964 myPlaylistEditor->Content->Swap(to, to+1);
965 myPlaylistEditor->Content->Scroll(wUp);
966 myPlaylistEditor->Content->Refresh();
967 wFooter->ReadKey(input);
969 if (from != to)
970 Mpd.Move(myPlaylistEditor->Playlists->Current(), from, to);
972 wFooter->SetTimeout(ncmpcpp_window_timeout);
975 else if (Keypressed(input, Key.MvSongDown))
977 if (myScreen == myPlaylist && myPlaylist->SortingInProgress())
978 myPlaylist->AdjustSortOrder(input);
979 else if (myScreen == myPlaylist && !myPlaylist->Items->Empty())
981 CHECK_PLAYLIST_FOR_FILTERING;
982 wFooter->SetTimeout(50);
983 if (myPlaylist->Items->hasSelected())
985 std::vector<size_t> list;
986 myPlaylist->Items->GetSelected(list);
987 std::vector<size_t> origs(list);
989 bool modify_now_playing = 0;
990 for (std::vector<size_t>::iterator it = list.begin(); it != list.end(); ++it)
992 if (*it == size_t(myPlaylist->NowPlaying) && list.back() < myPlaylist->Items->Size()-1)
994 modify_now_playing = 1;
995 Playlist::BlockNowPlayingUpdate = 1;
996 break;
1000 while (Keypressed(input, Key.MvSongDown) && list.back() < myPlaylist->Items->Size()-1)
1002 TraceMpdStatus();
1003 Playlist::BlockUpdate = 1;
1004 myPlaylist->UpdateTimer();
1005 if (modify_now_playing)
1006 ++myPlaylist->NowPlaying;
1007 for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it)
1009 ++*it;
1010 myPlaylist->Items->at((*it)-1).SetPosition(*it);
1011 myPlaylist->Items->at(*it).SetPosition((*it)-1);
1012 myPlaylist->Items->Swap(*it, (*it)-1);
1014 myPlaylist->Items->Highlight(list[(list.size()-1)/2]);
1015 myPlaylist->Items->Refresh();
1016 wFooter->ReadKey(input);
1018 Playlist::BlockNowPlayingUpdate = 0;
1019 Mpd.StartCommandsList();
1020 for (int i = list.size()-1; i >= 0; --i)
1021 Mpd.Move(origs[i], list[i]);
1022 Mpd.CommitCommandsList();
1024 else
1026 size_t from, to;
1027 from = to = myPlaylist->Items->Choice();
1028 bool modify_now_playing = from == size_t(myPlaylist->NowPlaying);
1029 if (modify_now_playing)
1030 Playlist::BlockNowPlayingUpdate = 1;
1031 while (Keypressed(input, Key.MvSongDown) && to < myPlaylist->Items->Size()-1)
1033 TraceMpdStatus();
1034 Playlist::BlockUpdate = 1;
1035 myPlaylist->UpdateTimer();
1036 if (modify_now_playing)
1037 ++myPlaylist->NowPlaying;
1038 ++to;
1039 myPlaylist->Items->at(from).SetPosition(to);
1040 myPlaylist->Items->at(to).SetPosition(from);
1041 myPlaylist->Items->Swap(to, to-1);
1042 myPlaylist->Items->Scroll(wDown);
1043 myPlaylist->Items->Refresh();
1044 wFooter->ReadKey(input);
1046 Mpd.Move(from, to);
1047 Playlist::BlockNowPlayingUpdate = 0;
1048 UpdateStatusImmediately = 1;
1050 wFooter->SetTimeout(ncmpcpp_window_timeout);
1053 else if (myScreen->ActiveWindow() == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty())
1055 wFooter->SetTimeout(50);
1056 if (myPlaylistEditor->Content->hasSelected())
1058 std::vector<size_t> list;
1059 myPlaylistEditor->Content->GetSelected(list);
1061 std::vector<size_t> origs(list);
1063 while (Keypressed(input, Key.MvSongDown) && list.back() < myPlaylistEditor->Content->Size()-1)
1065 TraceMpdStatus();
1066 myPlaylist->UpdateTimer();
1067 for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it)
1069 ++*it;
1070 myPlaylistEditor->Content->Swap(*it, (*it)-1);
1072 myPlaylistEditor->Content->Highlight(list[(list.size()-1)/2]);
1073 myPlaylistEditor->Content->Refresh();
1074 wFooter->ReadKey(input);
1076 Mpd.StartCommandsList();
1077 for (int i = list.size()-1; i >= 0; --i)
1078 if (origs[i] != list[i])
1079 Mpd.Move(myPlaylistEditor->Playlists->Current(), origs[i], list[i]);
1080 Mpd.CommitCommandsList();
1082 else
1084 size_t from, to;
1085 from = to = myPlaylistEditor->Content->Choice();
1086 while (Keypressed(input, Key.MvSongDown) && to < myPlaylistEditor->Content->Size()-1)
1088 TraceMpdStatus();
1089 myPlaylist->UpdateTimer();
1090 ++to;
1091 myPlaylistEditor->Content->Swap(to, to-1);
1092 myPlaylistEditor->Content->Scroll(wDown);
1093 myPlaylistEditor->Content->Refresh();
1094 wFooter->ReadKey(input);
1096 if (from != to)
1097 Mpd.Move(myPlaylistEditor->Playlists->Current(), from, to);
1099 wFooter->SetTimeout(ncmpcpp_window_timeout);
1102 else if (Keypressed(input, Key.MoveTo) && myScreen == myPlaylist)
1104 CHECK_PLAYLIST_FOR_FILTERING;
1105 if (!myPlaylist->Items->hasSelected())
1107 ShowMessage("No selected items to move!");
1108 continue;
1110 Playlist::BlockUpdate = 1;
1111 size_t pos = myPlaylist->Items->Choice();
1112 // if cursor is at the last item, break convention and move at the end of playlist
1113 if (pos == myPlaylist->Items->Size()-1)
1114 pos++;
1115 std::vector<size_t> list;
1116 myPlaylist->Items->GetSelected(list);
1117 if (pos >= list.front() && pos <= list.back())
1118 continue;
1119 int diff = pos-list.front();
1120 Mpd.StartCommandsList();
1121 if (diff > 0)
1123 pos -= list.size();
1124 size_t i = list.size()-1;
1125 for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it, --i)
1127 Mpd.Move(*it, pos+i);
1128 myPlaylist->Items->Move(*it, pos+i);
1131 else if (diff < 0)
1133 size_t i = 0;
1134 for (std::vector<size_t>::const_iterator it = list.begin(); it != list.end(); ++it, ++i)
1136 Mpd.Move(*it, pos+i);
1137 myPlaylist->Items->Move(*it, pos+i);
1140 Mpd.CommitCommandsList();
1141 myPlaylist->Items->Highlight(pos);
1142 myPlaylist->FixPositions();
1144 else if (Keypressed(input, Key.Add))
1146 if (myScreen == myPlaylistEditor && myPlaylistEditor->Playlists->Empty())
1147 continue;
1148 LockStatusbar();
1149 Statusbar() << (myScreen == myPlaylistEditor ? "Add to playlist: " : "Add: ");
1150 std::string path = wFooter->GetString();
1151 UnlockStatusbar();
1152 if (!path.empty())
1154 if (myScreen == myPlaylistEditor)
1156 Mpd.AddToPlaylist(myPlaylistEditor->Playlists->Current(), path);
1157 myPlaylistEditor->Content->Clear(); // make it refetch content of playlist
1159 else
1160 Mpd.Add(path);
1161 UpdateStatusImmediately = 1;
1164 else if (Keypressed(input, Key.SeekForward) || Keypressed(input, Key.SeekBackward))
1166 if (!Mpd.GetTotalTime())
1168 ShowMessage("Unknown item length!");
1169 continue;
1172 const Song *s = myPlaylist->NowPlayingSong();
1173 if (!s)
1174 continue;
1176 LockProgressbar();
1177 LockStatusbar();
1179 int songpos;
1180 time_t t = time(0);
1182 songpos = Mpd.GetElapsedTime();
1184 int old_timeout = wFooter->GetTimeout();
1185 wFooter->SetTimeout(ncmpcpp_window_timeout);
1187 SeekingInProgress = 1;
1188 *wFooter << fmtBold;
1189 while (Keypressed(input, Key.SeekForward) || Keypressed(input, Key.SeekBackward))
1191 TraceMpdStatus();
1192 myPlaylist->UpdateTimer();
1193 wFooter->ReadKey(input);
1195 int howmuch = Config.incremental_seeking ? (myPlaylist->Timer()-t)/2+Config.seek_time : Config.seek_time;
1197 if (Keypressed(input, Key.SeekForward) && songpos < Mpd.GetTotalTime())
1199 songpos += howmuch;
1200 if (songpos > Mpd.GetTotalTime())
1201 songpos = Mpd.GetTotalTime();
1203 else if (Keypressed(input, Key.SeekBackward) && songpos > 0)
1205 songpos -= howmuch;
1206 if (songpos < 0)
1207 songpos = 0;
1210 std::string tracklength;
1211 if (Config.new_design)
1213 if (Config.display_remaining_time)
1215 tracklength = "-";
1216 tracklength += Song::ShowTime(Mpd.GetTotalTime()-songpos);
1218 else
1219 tracklength = Song::ShowTime(songpos);
1220 tracklength += "/";
1221 tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime());
1222 *wHeader << XY(0, 0) << tracklength << " ";
1223 wHeader->Refresh();
1225 else
1227 tracklength = " [";
1228 if (Config.display_remaining_time)
1230 tracklength += "-";
1231 tracklength += Song::ShowTime(Mpd.GetTotalTime()-songpos);
1233 else
1234 tracklength += Song::ShowTime(songpos);
1235 tracklength += "/";
1236 tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime());
1237 tracklength += "]";
1238 *wFooter << XY(wFooter->GetWidth()-tracklength.length(), 1) << tracklength;
1240 DrawProgressbar(songpos, Mpd.GetTotalTime());
1241 wFooter->Refresh();
1243 *wFooter << fmtBoldEnd;
1244 SeekingInProgress = 0;
1245 Mpd.Seek(songpos);
1246 UpdateStatusImmediately = 1;
1248 wFooter->SetTimeout(old_timeout);
1250 UnlockProgressbar();
1251 UnlockStatusbar();
1253 else if (Keypressed(input, Key.ToggleDisplayMode))
1255 if (myScreen == myPlaylist)
1257 Config.columns_in_playlist = !Config.columns_in_playlist;
1258 ShowMessage("Playlist display mode: %s", Config.columns_in_playlist ? "Columns" : "Classic");
1260 if (Config.columns_in_playlist)
1262 myPlaylist->Items->SetItemDisplayer(Display::SongsInColumns);
1263 myPlaylist->Items->SetTitle(Display::Columns());
1264 myPlaylist->Items->SetGetStringFunction(Playlist::SongInColumnsToString);
1266 else
1268 myPlaylist->Items->SetItemDisplayer(Display::Songs);
1269 myPlaylist->Items->SetTitle("");
1270 myPlaylist->Items->SetGetStringFunction(Playlist::SongToString);
1273 else if (myScreen == myBrowser)
1275 Config.columns_in_browser = !Config.columns_in_browser;
1276 ShowMessage("Browser display mode: %s", Config.columns_in_browser ? "Columns" : "Classic");
1277 myBrowser->Main()->SetTitle(Config.columns_in_browser ? Display::Columns() : "");
1280 else if (myScreen == mySearcher)
1282 Config.columns_in_search_engine = !Config.columns_in_search_engine;
1283 ShowMessage("Search engine display mode: %s", Config.columns_in_search_engine ? "Columns" : "Classic");
1284 if (mySearcher->Main()->Size() > SearchEngine::StaticOptions)
1285 mySearcher->Main()->SetTitle(Config.columns_in_search_engine ? Display::Columns() : "");
1288 # ifdef HAVE_CURL_CURL_H
1289 else if (Keypressed(input, Key.ToggleLyricsDB))
1291 const char *current_lyrics_plugin = Lyrics::GetPluginName(++Config.lyrics_db);
1292 if (!current_lyrics_plugin)
1294 current_lyrics_plugin = Lyrics::GetPluginName(Config.lyrics_db = 0);
1296 ShowMessage("Using lyrics database: %s", current_lyrics_plugin);
1298 # endif // HAVE_CURL_CURL_H
1299 else if (Keypressed(input, Key.ToggleAutoCenter))
1301 Config.autocenter_mode = !Config.autocenter_mode;
1302 ShowMessage("Auto center mode: %s", Config.autocenter_mode ? "On" : "Off");
1303 if (Config.autocenter_mode && myPlaylist->isPlaying() && !myPlaylist->Items->isFiltered())
1304 myPlaylist->Items->Highlight(myPlaylist->NowPlaying);
1306 else if (Keypressed(input, Key.UpdateDB))
1308 if (myScreen == myBrowser)
1309 Mpd.UpdateDirectory(locale_to_utf_cpy(myBrowser->CurrentDir()));
1310 # ifdef HAVE_TAGLIB_H
1311 else if (myScreen == myTagEditor && !Config.albums_in_tag_editor)
1312 Mpd.UpdateDirectory(myTagEditor->CurrentDir());
1313 # endif // HAVE_TAGLIB_H
1314 else
1315 Mpd.UpdateDirectory("/");
1317 else if (Keypressed(input, Key.GoToNowPlaying))
1319 if (myScreen == myPlaylist && myPlaylist->isPlaying())
1321 CHECK_PLAYLIST_FOR_FILTERING;
1322 myPlaylist->Items->Highlight(myPlaylist->NowPlaying);
1324 else if (myScreen == myBrowser)
1326 if (const Song *s = myPlaylist->NowPlayingSong())
1328 myBrowser->LocateSong(*s);
1329 RedrawHeader = 1;
1333 else if (Keypressed(input, Key.ToggleRepeat))
1335 Mpd.SetRepeat(!Mpd.GetRepeat());
1336 UpdateStatusImmediately = 1;
1338 else if (Keypressed(input, Key.Shuffle))
1340 Mpd.Shuffle();
1341 UpdateStatusImmediately = 1;
1343 else if (Keypressed(input, Key.ToggleRandom))
1345 Mpd.SetRandom(!Mpd.GetRandom());
1346 UpdateStatusImmediately = 1;
1348 else if (Keypressed(input, Key.ToggleSingle))
1350 if (myScreen == mySearcher && !mySearcher->Main()->isStatic(0))
1352 mySearcher->Main()->Highlight(SearchEngine::SearchButton);
1353 mySearcher->Main()->Highlighting(0);
1354 mySearcher->Main()->Refresh();
1355 mySearcher->Main()->Highlighting(1);
1356 mySearcher->EnterPressed();
1358 # ifdef HAVE_TAGLIB_H
1359 else if (myScreen == myTinyTagEditor)
1361 myTinyTagEditor->Main()->Highlight(myTinyTagEditor->Main()->Size()-2); // Save
1362 myTinyTagEditor->EnterPressed();
1364 # endif // HAVE_TAGLIB_H
1365 else
1367 Mpd.SetSingle(!Mpd.GetSingle());
1368 UpdateStatusImmediately = 1;
1371 else if (Keypressed(input, Key.ToggleConsume))
1373 Mpd.SetConsume(!Mpd.GetConsume());
1374 UpdateStatusImmediately = 1;
1376 else if (Keypressed(input, Key.ToggleCrossfade))
1378 Mpd.SetCrossfade(Mpd.GetCrossfade() ? 0 : Config.crossfade_time);
1379 UpdateStatusImmediately = 1;
1381 else if (Keypressed(input, Key.SetCrossfade))
1383 LockStatusbar();
1384 Statusbar() << "Set crossfade to: ";
1385 std::string crossfade = wFooter->GetString(3);
1386 UnlockStatusbar();
1387 int cf = StrToInt(crossfade);
1388 if (cf > 0)
1390 Config.crossfade_time = cf;
1391 Mpd.SetCrossfade(cf);
1392 UpdateStatusImmediately = 1;
1395 else if (Keypressed(input, Key.EditTags))
1397 if ((myScreen != myBrowser || !myBrowser->isLocal())
1398 && myScreen != myLyrics)
1399 CHECK_MPD_MUSIC_DIR;
1400 # ifdef HAVE_TAGLIB_H
1401 if (myTinyTagEditor->SetEdited(myScreen->CurrentSong()))
1403 myTinyTagEditor->SwitchTo();
1405 else if (myScreen->ActiveWindow() == myLibrary->Artists)
1407 LockStatusbar();
1408 Statusbar() << fmtBold << IntoStr(Config.media_lib_primary_tag) << fmtBoldEnd << ": ";
1409 std::string new_tag = wFooter->GetString(myLibrary->Artists->Current());
1410 UnlockStatusbar();
1411 if (!new_tag.empty() && new_tag != myLibrary->Artists->Current())
1413 bool success = 1;
1414 SongList list;
1415 ShowMessage("Updating tags...");
1416 Mpd.StartSearch(1);
1417 Mpd.AddSearch(Config.media_lib_primary_tag, locale_to_utf_cpy(myLibrary->Artists->Current()));
1418 Mpd.CommitSearch(list);
1419 Song::SetFunction set = IntoSetFunction(Config.media_lib_primary_tag);
1420 if (!set)
1421 continue;
1422 for (SongList::iterator it = list.begin(); it != list.end(); ++it)
1424 (*it)->Localize();
1425 (*it)->SetTags(set, new_tag);
1426 ShowMessage("Updating tags in \"%s\"...", (*it)->GetName().c_str());
1427 std::string path = Config.mpd_music_dir + (*it)->GetFile();
1428 if (!TagEditor::WriteTags(**it))
1430 static const char msg[] = "Error while updating tags in \"%s\"!";
1431 ShowMessage(msg, Shorten(TO_WSTRING((*it)->GetFile()), COLS-static_strlen(msg)).c_str());
1432 success = 0;
1433 break;
1436 if (success)
1438 Mpd.UpdateDirectory(locale_to_utf_cpy(FindSharedDir(list)));
1439 ShowMessage("Tags updated succesfully!");
1441 FreeSongList(list);
1444 else if (myScreen->ActiveWindow() == myLibrary->Albums)
1446 LockStatusbar();
1447 Statusbar() << fmtBold << "Album: " << fmtBoldEnd;
1448 std::string new_album = wFooter->GetString(myLibrary->Albums->Current().second.Album);
1449 UnlockStatusbar();
1450 if (!new_album.empty() && new_album != myLibrary->Albums->Current().second.Album)
1452 bool success = 1;
1453 ShowMessage("Updating tags...");
1454 for (size_t i = 0; i < myLibrary->Songs->Size(); ++i)
1456 (*myLibrary->Songs)[i].Localize();
1457 ShowMessage("Updating tags in \"%s\"...", (*myLibrary->Songs)[i].GetName().c_str());
1458 std::string path = Config.mpd_music_dir + (*myLibrary->Songs)[i].GetFile();
1459 TagLib::FileRef f(locale_to_utf_cpy(path).c_str());
1460 if (f.isNull())
1462 static const char msg[] = "Error while opening file \"%s\"!";
1463 ShowMessage(msg, Shorten(TO_WSTRING((*myLibrary->Songs)[i].GetFile()), COLS-static_strlen(msg)).c_str());
1464 success = 0;
1465 break;
1467 f.tag()->setAlbum(ToWString(new_album));
1468 if (!f.save())
1470 static const char msg[] = "Error while writing tags in \"%s\"!";
1471 ShowMessage(msg, Shorten(TO_WSTRING((*myLibrary->Songs)[i].GetFile()), COLS-static_strlen(msg)).c_str());
1472 success = 0;
1473 break;
1476 if (success)
1478 Mpd.UpdateDirectory(locale_to_utf_cpy(FindSharedDir(myLibrary->Songs)));
1479 ShowMessage("Tags updated succesfully!");
1483 else if (myScreen->ActiveWindow() == myTagEditor->Dirs)
1485 std::string old_dir = myTagEditor->Dirs->Current().first;
1486 LockStatusbar();
1487 Statusbar() << fmtBold << "Directory: " << fmtBoldEnd;
1488 std::string new_dir = wFooter->GetString(old_dir);
1489 UnlockStatusbar();
1490 if (!new_dir.empty() && new_dir != old_dir)
1492 std::string full_old_dir = Config.mpd_music_dir + myTagEditor->CurrentDir() + "/" + locale_to_utf_cpy(old_dir);
1493 std::string full_new_dir = Config.mpd_music_dir + myTagEditor->CurrentDir() + "/" + locale_to_utf_cpy(new_dir);
1494 if (rename(full_old_dir.c_str(), full_new_dir.c_str()) == 0)
1496 static const char msg[] = "Directory renamed to \"%s\"";
1497 ShowMessage(msg, Shorten(TO_WSTRING(new_dir), COLS-static_strlen(msg)).c_str());
1498 Mpd.UpdateDirectory(myTagEditor->CurrentDir());
1500 else
1502 static const char msg[] = "Couldn't rename \"%s\": %s";
1503 ShowMessage(msg, Shorten(TO_WSTRING(old_dir), COLS-static_strlen(msg)-25).c_str(), strerror(errno));
1507 else
1508 # endif // HAVE_TAGLIB_H
1509 if (myScreen == myLyrics)
1511 myLyrics->Edit();
1513 if (myScreen == myBrowser && myBrowser->Main()->Current().type == itDirectory)
1515 std::string old_dir = myBrowser->Main()->Current().name;
1516 LockStatusbar();
1517 Statusbar() << fmtBold << "Directory: " << fmtBoldEnd;
1518 std::string new_dir = wFooter->GetString(old_dir);
1519 UnlockStatusbar();
1520 if (!new_dir.empty() && new_dir != old_dir)
1522 std::string full_old_dir;
1523 if (!myBrowser->isLocal())
1524 full_old_dir += Config.mpd_music_dir;
1525 full_old_dir += locale_to_utf_cpy(old_dir);
1526 std::string full_new_dir;
1527 if (!myBrowser->isLocal())
1528 full_new_dir += Config.mpd_music_dir;
1529 full_new_dir += locale_to_utf_cpy(new_dir);
1530 int rename_result = rename(full_old_dir.c_str(), full_new_dir.c_str());
1531 if (rename_result == 0)
1533 static const char msg[] = "Directory renamed to \"%s\"";
1534 ShowMessage(msg, Shorten(TO_WSTRING(new_dir), COLS-static_strlen(msg)).c_str());
1535 if (!myBrowser->isLocal())
1536 Mpd.UpdateDirectory(locale_to_utf_cpy(FindSharedDir(old_dir, new_dir)));
1537 myBrowser->GetDirectory(myBrowser->CurrentDir());
1539 else
1541 static const char msg[] = "Couldn't rename \"%s\": %s";
1542 ShowMessage(msg, Shorten(TO_WSTRING(old_dir), COLS-static_strlen(msg)-25).c_str(), strerror(errno));
1546 else if (myScreen->ActiveWindow() == myPlaylistEditor->Playlists || (myScreen == myBrowser && myBrowser->Main()->Current().type == itPlaylist))
1548 std::string old_name = myScreen->ActiveWindow() == myPlaylistEditor->Playlists ? myPlaylistEditor->Playlists->Current() : myBrowser->Main()->Current().name;
1549 LockStatusbar();
1550 Statusbar() << fmtBold << "Playlist: " << fmtBoldEnd;
1551 std::string new_name = wFooter->GetString(old_name);
1552 UnlockStatusbar();
1553 if (!new_name.empty() && new_name != old_name)
1555 if (Mpd.Rename(locale_to_utf_cpy(old_name), locale_to_utf_cpy(new_name)))
1557 static const char msg[] = "Playlist renamed to \"%s\"";
1558 ShowMessage(msg, Shorten(TO_WSTRING(new_name), COLS-static_strlen(msg)).c_str());
1559 if (myBrowser->Main() && !myBrowser->isLocal())
1560 myBrowser->GetDirectory("/");
1561 if (myPlaylistEditor->Main())
1562 myPlaylistEditor->Playlists->Clear();
1567 else if (Keypressed(input, Key.GoToContainingDir))
1569 Song *s = myScreen->CurrentSong();
1570 if (s)
1571 myBrowser->LocateSong(*s);
1573 else if (Keypressed(input, Key.GoToPosition))
1575 if (!Mpd.GetTotalTime())
1577 ShowMessage("Unknown item length!");
1578 continue;
1581 const Song *s = myPlaylist->NowPlayingSong();
1582 if (!s)
1583 continue;
1585 LockStatusbar();
1586 Statusbar() << "Position to go (in %/mm:ss/seconds(s)): ";
1587 std::string position = wFooter->GetString();
1588 UnlockStatusbar();
1590 if (position.empty())
1591 continue;
1593 int newpos = 0;
1594 if (position.find(':') != std::string::npos) // probably time in mm:ss
1596 newpos = StrToInt(position)*60 + StrToInt(position.substr(position.find(':')+1));
1597 if (newpos >= 0 && newpos <= Mpd.GetTotalTime())
1598 Mpd.Seek(newpos);
1599 else
1600 ShowMessage("Out of bounds, 0:00-%s possible for mm:ss, %s given.", s->GetLength().c_str(), MPD::Song::ShowTime(newpos).c_str());
1602 else if (position.find('s') != std::string::npos) // probably position in seconds
1604 newpos = StrToInt(position);
1605 if (newpos >= 0 && newpos <= Mpd.GetTotalTime())
1606 Mpd.Seek(newpos);
1607 else
1608 ShowMessage("Out of bounds, 0-%d possible for seconds, %d given.", s->GetTotalLength(), newpos);
1610 else
1612 newpos = StrToInt(position);
1613 if (newpos >= 0 && newpos <= 100)
1614 Mpd.Seek(Mpd.GetTotalTime()*newpos/100.0);
1615 else
1616 ShowMessage("Out of bounds, 0-100 possible for %%, %d given.", newpos);
1618 UpdateStatusImmediately = 1;
1620 else if (Keypressed(input, Key.ReverseSelection))
1622 if (myScreen->allowsSelection())
1624 myScreen->ReverseSelection();
1625 ShowMessage("Selection reversed!");
1628 else if (Keypressed(input, Key.DeselectAll))
1630 if (myScreen->allowsSelection())
1632 List *mList = myScreen->GetList();
1633 if (!mList->hasSelected())
1634 continue;
1635 for (size_t i = 0; i < mList->Size(); ++i)
1636 mList->Select(i, 0);
1637 ShowMessage("Items deselected!");
1640 else if (Keypressed(input, Key.AddSelected))
1642 mySelectedItemsAdder->SwitchTo();
1644 else if (Keypressed(input, Key.Crop))
1646 CHECK_PLAYLIST_FOR_FILTERING;
1647 if (myPlaylist->Items->hasSelected())
1649 Mpd.StartCommandsList();
1650 for (int i = myPlaylist->Items->Size()-1; i >= 0; --i)
1652 if (!myPlaylist->Items->isSelected(i) && i != myPlaylist->NowPlaying)
1653 Mpd.Delete(i);
1655 // if mpd deletes now playing song deletion will be sluggishly slow
1656 // then so we have to assure it will be deleted at the very end.
1657 if (myPlaylist->isPlaying() && !myPlaylist->Items->isSelected(myPlaylist->NowPlaying))
1658 Mpd.DeleteID(myPlaylist->NowPlayingSong()->GetID());
1660 ShowMessage("Deleting all items but selected...");
1661 Mpd.CommitCommandsList();
1662 ShowMessage("Items deleted!");
1664 else
1666 if (!myPlaylist->isPlaying())
1668 ShowMessage("Nothing is playing now!");
1669 continue;
1671 Mpd.StartCommandsList();
1672 for (int i = myPlaylist->Items->Size()-1; i >= 0; --i)
1673 if (i != myPlaylist->NowPlaying)
1674 Mpd.Delete(i);
1675 ShowMessage("Deleting all items except now playing one...");
1676 Mpd.CommitCommandsList();
1677 ShowMessage("Items deleted!");
1680 else if (Keypressed(input, Key.Clear))
1682 if (myScreen == myPlaylistEditor && myPlaylistEditor->Playlists->Empty())
1683 continue;
1685 if (myScreen->ActiveWindow() == myPlaylistEditor->Content
1686 || Config.ask_before_clearing_main_playlist)
1688 LockStatusbar();
1689 Statusbar() << "Do you really want to clear playlist";
1690 if (myScreen->ActiveWindow() == myPlaylistEditor->Content)
1691 *wFooter << " \"" << myPlaylistEditor->Playlists->Current() << "\"";
1692 *wFooter << " ? [" << fmtBold << 'y' << fmtBoldEnd << '/' << fmtBold << 'n' << fmtBoldEnd << "] ";
1693 wFooter->Refresh();
1694 input = 0;
1697 TraceMpdStatus();
1698 wFooter->ReadKey(input);
1700 while (input != 'y' && input != 'n');
1701 UnlockStatusbar();
1702 if (input != 'y')
1704 ShowMessage("Aborted!");
1705 continue;
1709 if (myPlaylist->Items->isFiltered())
1711 ShowMessage("Deleting filtered items...");
1712 Mpd.StartCommandsList();
1713 for (int i = myPlaylist->Items->Size()-1; i >= 0; --i)
1714 Mpd.Delete((*myPlaylist->Items)[i].GetPosition());
1715 Mpd.CommitCommandsList();
1716 ShowMessage("Filtered items deleted!");
1718 else
1720 if (myScreen->ActiveWindow() == myPlaylistEditor->Content)
1722 Mpd.ClearPlaylist(locale_to_utf_cpy(myPlaylistEditor->Playlists->Current()));
1723 myPlaylistEditor->Content->Clear();
1725 else
1727 ShowMessage("Clearing playlist...");
1728 Mpd.ClearPlaylist();
1731 // if playlist is cleared, items list have to be updated, but this
1732 // can be blocked if new song was added to playlist less than one
1733 // second ago, so we need to assume it's unlocked.
1734 BlockItemListUpdate = 0;
1735 UpdateStatusImmediately = 1;
1737 else if (Keypressed(input, Key.SortPlaylist) && myScreen == myPlaylist)
1739 CHECK_PLAYLIST_FOR_FILTERING;
1740 myPlaylist->Sort();
1742 else if (Keypressed(input, Key.ApplyFilter))
1744 List *mList = myScreen->GetList();
1746 if (!mList)
1747 continue;
1749 LockStatusbar();
1750 Statusbar() << fmtBold << "Apply filter: " << fmtBoldEnd;
1751 wFooter->SetGetStringHelper(StatusbarApplyFilterImmediately);
1752 wFooter->GetString(mList->GetFilter());
1753 wFooter->SetGetStringHelper(StatusbarGetStringHelper);
1754 UnlockStatusbar();
1756 if (mList->isFiltered())
1757 ShowMessage("Using filter \"%s\"", mList->GetFilter().c_str());
1758 else
1759 ShowMessage("Filtering disabled");
1761 if (myScreen == myPlaylist)
1763 myPlaylist->EnableHighlighting();
1764 Playlist::ReloadTotalLength = 1;
1765 RedrawHeader = 1;
1768 else if (Keypressed(input, Key.FindForward) || Keypressed(input, Key.FindBackward))
1770 List *mList = myScreen->GetList();
1772 if (mList)
1774 LockStatusbar();
1775 Statusbar() << "Find " << (Keypressed(input, Key.FindForward) ? "forward" : "backward") << ": ";
1776 std::string findme = wFooter->GetString();
1777 UnlockStatusbar();
1778 myPlaylist->UpdateTimer();
1780 if (!findme.empty())
1781 ShowMessage("Searching...");
1783 bool success = mList->Search(findme, myScreen == mySearcher ? SearchEngine::StaticOptions : 0, REG_ICASE | Config.regex_type);
1785 if (findme.empty())
1786 continue;
1788 success ? ShowMessage("Searching finished!") : ShowMessage("Unable to find \"%s\"", findme.c_str());
1790 if (Keypressed(input, Key.FindForward))
1791 mList->NextFound(Config.wrapped_search);
1792 else
1793 mList->PrevFound(Config.wrapped_search);
1795 if (myScreen == myPlaylist)
1796 myPlaylist->EnableHighlighting();
1798 else if (myScreen == myHelp || myScreen == myLyrics || myScreen == myInfo)
1800 LockStatusbar();
1801 Statusbar() << "Find: ";
1802 std::string findme = wFooter->GetString();
1803 UnlockStatusbar();
1805 ShowMessage("Searching...");
1806 Screen<Scrollpad> *s = static_cast<Screen<Scrollpad> *>(myScreen);
1807 s->Main()->RemoveFormatting();
1808 ShowMessage("%s", findme.empty() || s->Main()->SetFormatting(fmtReverse, TO_WSTRING(findme), fmtReverseEnd) ? "Done!" : "No matching patterns found");
1809 s->Main()->Flush();
1812 else if (Keypressed(input, Key.NextFoundPosition) || Keypressed(input, Key.PrevFoundPosition))
1814 List *mList = myScreen->GetList();
1816 if (!mList)
1817 continue;
1819 if (Keypressed(input, Key.NextFoundPosition))
1820 mList->NextFound(Config.wrapped_search);
1821 else
1822 mList->PrevFound(Config.wrapped_search);
1824 else if (Keypressed(input, Key.ToggleFindMode))
1826 Config.wrapped_search = !Config.wrapped_search;
1827 ShowMessage("Search mode: %s", Config.wrapped_search ? "Wrapped" : "Normal");
1829 else if (Keypressed(input, Key.ToggleReplayGainMode) && Mpd.Version() >= 16)
1831 LockStatusbar();
1832 Statusbar() << "Replay gain mode ? [" << fmtBold << 'o' << fmtBoldEnd << "ff/" << fmtBold << 't' << fmtBoldEnd << "rack/" << fmtBold << 'a' << fmtBoldEnd << "lbum]";
1833 wFooter->Refresh();
1834 input = 0;
1837 TraceMpdStatus();
1838 wFooter->ReadKey(input);
1840 while (input != 'o' && input != 't' && input != 'a');
1841 UnlockStatusbar();
1842 Mpd.SetReplayGainMode(input == 't' ? rgmTrack : (input == 'a' ? rgmAlbum : rgmOff));
1843 ShowMessage("Replay gain mode: %s", Mpd.GetReplayGainMode().c_str());
1845 else if (Keypressed(input, Key.ToggleSpaceMode))
1847 Config.space_selects = !Config.space_selects;
1848 ShowMessage("Space mode: %s item", Config.space_selects ? "Select/deselect" : "Add");
1850 else if (Keypressed(input, Key.ToggleAddMode))
1852 Config.ncmpc_like_songs_adding = !Config.ncmpc_like_songs_adding;
1853 ShowMessage("Add mode: %s", Config.ncmpc_like_songs_adding ? "Add item to playlist, remove if already added" : "Always add item to playlist");
1855 else if (Keypressed(input, Key.ToggleMouse))
1857 Config.mouse_support = !Config.mouse_support;
1858 mousemask(Config.mouse_support ? ALL_MOUSE_EVENTS : 0, 0);
1859 ShowMessage("Mouse support %s", Config.mouse_support ? "enabled" : "disabled");
1861 else if (Keypressed(input, Key.SwitchTagTypeList))
1863 if (myScreen == myPlaylist)
1865 LockStatusbar();
1866 Statusbar() << "Number of random songs: ";
1867 size_t number = StrToLong(wFooter->GetString());
1868 UnlockStatusbar();
1869 if (number && Mpd.AddRandomSongs(number))
1870 ShowMessage("%zu random song%s added to playlist!", number, number == 1 ? "" : "s");
1872 else if (myScreen == myBrowser && !myBrowser->isLocal())
1874 Config.browser_sort_by_mtime = !Config.browser_sort_by_mtime;
1875 myBrowser->Main()->Sort<CaseInsensitiveSorting>(myBrowser->CurrentDir() != "/");
1876 ShowMessage("Sort songs by: %s", Config.browser_sort_by_mtime ? "Modification time" : "Name");
1878 else if (myScreen->ActiveWindow() == myLibrary->Artists
1879 || (myLibrary->Columns() == 2 && myScreen->ActiveWindow() == myLibrary->Albums))
1881 LockStatusbar();
1882 Statusbar() << "Tag type ? [" << fmtBold << 'a' << fmtBoldEnd << "rtist/" << fmtBold << 'y' << fmtBoldEnd << "ear/" << fmtBold << 'g' << fmtBoldEnd << "enre/" << fmtBold << 'c' << fmtBoldEnd << "omposer/" << fmtBold << 'p' << fmtBoldEnd << "erformer] ";
1883 wFooter->Refresh();
1884 input = 0;
1887 TraceMpdStatus();
1888 wFooter->ReadKey(input);
1890 while (input != 'a' && input != 'y' && input != 'g' && input != 'c' && input != 'p');
1891 UnlockStatusbar();
1892 mpd_tag_type new_tagitem = IntoTagItem(input);
1893 if (new_tagitem != Config.media_lib_primary_tag)
1895 Config.media_lib_primary_tag = new_tagitem;
1896 std::string item_type = IntoStr(Config.media_lib_primary_tag);
1897 myLibrary->Artists->SetTitle(item_type + "s");
1898 myLibrary->Artists->Reset();
1899 ToLower(item_type);
1900 if (myLibrary->Columns() == 2)
1902 myLibrary->Songs->Clear();
1903 myLibrary->Albums->Reset();
1904 myLibrary->Albums->Clear();
1905 myLibrary->Albums->SetTitle("Albums (sorted by " + item_type + ")");
1906 myLibrary->Albums->Display();
1908 else
1910 myLibrary->Artists->Clear();
1911 myLibrary->Artists->Display();
1913 ShowMessage("Switched to list of %s tag", item_type.c_str());
1916 else if (myScreen == myLyrics)
1918 myLyrics->FetchAgain();
1921 else if (Keypressed(input, Key.SongInfo))
1923 myInfo->GetSong();
1925 # ifdef HAVE_CURL_CURL_H
1926 else if (Keypressed(input, Key.ArtistInfo))
1928 myInfo->GetArtist();
1930 # endif // HAVE_CURL_CURL_H
1931 else if (Keypressed(input, Key.Lyrics))
1933 myLyrics->SwitchTo();
1935 else if (Keypressed(input, Key.Quit))
1937 main_exit = 1;
1939 # ifdef HAVE_TAGLIB_H
1940 else if (myScreen == myTinyTagEditor)
1942 continue;
1944 # endif // HAVE_TAGLIB_H
1945 else if (Keypressed(input, Key.Help))
1947 myHelp->SwitchTo();
1949 else if (Keypressed(input, Key.ScreenSwitcher))
1951 if (myScreen == myPlaylist)
1952 myBrowser->SwitchTo();
1953 else
1954 myPlaylist->SwitchTo();
1956 else if (Keypressed(input, Key.Playlist))
1958 myPlaylist->SwitchTo();
1960 else if (Keypressed(input, Key.Browser))
1962 myBrowser->SwitchTo();
1964 else if (Keypressed(input, Key.SearchEngine))
1966 mySearcher->SwitchTo();
1968 else if (Keypressed(input, Key.MediaLibrary))
1970 myLibrary->SwitchTo();
1972 else if (Keypressed(input, Key.PlaylistEditor))
1974 myPlaylistEditor->SwitchTo();
1976 # ifdef HAVE_TAGLIB_H
1977 else if (Keypressed(input, Key.TagEditor))
1979 CHECK_MPD_MUSIC_DIR;
1980 myTagEditor->SwitchTo();
1982 # endif // HAVE_TAGLIB_H
1983 # ifdef ENABLE_OUTPUTS
1984 else if (Keypressed(input, Key.Outputs))
1986 myOutputs->SwitchTo();
1988 # endif // ENABLE_OUTPUTS
1989 # ifdef ENABLE_VISUALIZER
1990 else if (Keypressed(input, Key.Visualizer))
1992 myVisualizer->SwitchTo();
1994 # endif // ENABLE_VISUALIZER
1995 # ifdef ENABLE_CLOCK
1996 else if (Keypressed(input, Key.Clock))
1998 myClock->SwitchTo();
2000 # endif // ENABLE_CLOCK
2001 else if (Keypressed(input, Key.ServerInfo))
2003 myServerInfo->SwitchTo();
2005 // key mapping end
2007 # ifdef ENABLE_VISUALIZER
2008 // visualizer sets timmeout to 40ms, but since only it needs such small
2009 // value, we should restore defalt one after switching to another screen.
2010 if (wFooter->GetTimeout() < ncmpcpp_window_timeout && myScreen != myVisualizer)
2011 wFooter->SetTimeout(ncmpcpp_window_timeout);
2012 # endif // ENABLE_VISUALIZER
2014 return 0;