move code responsible for screen resize to SIGWINCH handler
[ncmpcpp.git] / src / ncmpcpp.cpp
blobad0b3a827c4dccfa5d8fe49e537f153597218377
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 size_t header_height, footer_start_y, footer_height;
97 void sighandler(GNUC_UNUSED int signal)
99 # if !defined(WIN32)
100 if (signal == SIGPIPE)
102 ShowMessage("Broken pipe signal caught!");
103 return;
105 # endif // !WIN32
107 # if defined(USE_PDCURSES)
108 resize_term(0, 0);
109 # else
110 // update internal screen dimensions
111 if (!design_changed)
113 endwin();
114 refresh();
115 // get rid of KEY_RESIZE as it sometimes
116 // corrupts our new cool ReadKey() function
117 getch();
119 # endif
121 RedrawHeader = 1;
122 MainHeight = LINES-(Config.new_design ? 7 : 4);
124 if (COLS < 20 || MainHeight < 3)
126 DestroyScreen();
127 std::cout << "Screen is too small!\n";
128 exit(1);
131 if (!Config.header_visibility)
132 MainHeight += 2;
133 if (!Config.statusbar_visibility)
134 MainHeight++;
136 myHelp->hasToBeResized = 1;
137 myPlaylist->hasToBeResized = 1;
138 myBrowser->hasToBeResized = 1;
139 mySearcher->hasToBeResized = 1;
140 myLibrary->hasToBeResized = 1;
141 myPlaylistEditor->hasToBeResized = 1;
142 myInfo->hasToBeResized = 1;
143 myLyrics->hasToBeResized = 1;
144 mySelectedItemsAdder->hasToBeResized = 1;
146 # ifdef HAVE_TAGLIB_H
147 myTinyTagEditor->hasToBeResized = 1;
148 myTagEditor->hasToBeResized = 1;
149 # endif // HAVE_TAGLIB_H
151 # ifdef ENABLE_VISUALIZER
152 myVisualizer->hasToBeResized = 1;
153 # endif // ENABLE_VISUALIZER
155 # ifdef ENABLE_OUTPUTS
156 myOutputs->hasToBeResized = 1;
157 # endif // ENABLE_OUTPUTS
159 # ifdef ENABLE_CLOCK
160 myClock->hasToBeResized = 1;
161 # endif // ENABLE_CLOCK
163 myScreen->Resize();
165 if (Config.header_visibility || Config.new_design)
166 wHeader->Resize(COLS, header_height);
168 footer_start_y = LINES-(Config.statusbar_visibility ? 2 : 1);
169 wFooter->MoveTo(0, footer_start_y);
170 wFooter->Resize(COLS, Config.statusbar_visibility ? 2 : 1);
172 myScreen->Refresh();
173 RedrawStatusbar = 1;
174 StatusChanges changes;
175 if (!Mpd.isPlaying() || design_changed)
177 changes.PlayerState = 1;
178 if (design_changed)
179 changes.Volume = 1;
181 // Note: routines for drawing separator if alternative user
182 // interface is active and header is hidden are placed in
183 // NcmpcppStatusChanges.StatusFlags
184 changes.StatusFlags = 1; // force status update
185 NcmpcppStatusChanged(&Mpd, changes, 0);
186 if (design_changed)
188 RedrawStatusbar = 1;
189 NcmpcppStatusChanged(&Mpd, StatusChanges(), 0);
190 design_changed = 0;
191 ShowMessage("User interface: %s", Config.new_design ? "Alternative" : "Classic");
193 wFooter->Refresh();
196 void do_at_exit()
198 // restore old cerr buffer
199 std::cerr.rdbuf(cerr_buffer);
200 errorlog.close();
201 Mpd.Disconnect();
202 # ifndef USE_PDCURSES // destroying screen somehow crashes pdcurses
203 DestroyScreen();
204 # endif // USE_PDCURSES
205 WindowTitle("");
209 int main(int argc, char *argv[])
211 setlocale(LC_ALL, "");
213 CreateConfigDir();
214 DefaultConfiguration(Config);
215 DefaultKeys(Key);
216 ReadConfiguration(Config);
217 ReadKeys(Key);
219 if (getenv("MPD_HOST"))
220 Mpd.SetHostname(getenv("MPD_HOST"));
221 if (getenv("MPD_PORT"))
222 Mpd.SetPort(atoi(getenv("MPD_PORT")));
224 if (Config.mpd_host != "localhost")
225 Mpd.SetHostname(Config.mpd_host);
226 if (Config.mpd_port != 6600)
227 Mpd.SetPort(Config.mpd_port);
229 Mpd.SetTimeout(Config.mpd_connection_timeout);
231 if (argc > 1)
232 ParseArgv(argc, argv);
234 if (!ConnectToMPD())
235 return -1;
237 // always execute these commands, even if ncmpcpp use exit function
238 atexit(do_at_exit);
240 // redirect std::cerr output to ~/.ncmpcpp/error.log file
241 errorlog.open((config_dir + "error.log").c_str(), std::ios::app);
242 cerr_buffer = std::cerr.rdbuf();
243 std::cerr.rdbuf(errorlog.rdbuf());
245 InitScreen("ncmpc++ ver. "VERSION, Config.colors_enabled);
247 bool real_statusbar_visibility = Config.statusbar_visibility;
249 if (Config.new_design)
250 Config.statusbar_visibility = 0;
252 SetWindowsDimensions(header_height, footer_start_y, footer_height);
254 if (Config.header_visibility || Config.new_design)
256 wHeader = new Window(0, 0, COLS, header_height, "", Config.header_color, brNone);
257 wHeader->Display();
260 wFooter = new Window(0, footer_start_y, COLS, footer_height, "", Config.statusbar_color, brNone);
261 wFooter->SetTimeout(ncmpcpp_window_timeout);
262 wFooter->SetGetStringHelper(StatusbarGetStringHelper);
263 wFooter->AddFDCallback(Mpd.GetFD(), StatusbarMPDCallback);
264 wFooter->CreateHistory();
266 myPlaylist->SwitchTo();
267 myPlaylist->UpdateTimer();
269 Mpd.SetStatusUpdater(NcmpcppStatusChanged, 0);
270 Mpd.SetErrorHandler(NcmpcppErrorCallback, 0);
272 // local variables
273 int input = 0;
275 bool main_exit = 0;
276 bool title_allowed = !Config.display_screens_numbers_on_start;
278 std::string screen_title;
280 timeval now, past;
281 // local variables end
283 # ifndef WIN32
284 signal(SIGPIPE, sighandler);
285 signal(SIGWINCH, sighandler);
286 # endif // !WIN32
288 gettimeofday(&now, 0);
290 MEVENT mouse_event;
291 mouseinterval(0);
292 if (Config.mouse_support)
293 mousemask(ALL_MOUSE_EVENTS, 0);
295 if (Config.jump_to_now_playing_song_at_start)
297 TraceMpdStatus();
298 if (myPlaylist->isPlaying())
299 myPlaylist->Items->Highlight(myPlaylist->NowPlaying);
302 while (!main_exit)
304 if (!Mpd.Connected())
306 if (!wFooter->FDCallbacksListEmpty())
307 wFooter->ClearFDCallbacksList();
308 ShowMessage("Attempting to reconnect...");
309 if (Mpd.Connect())
311 ShowMessage("Connected to %s!", Mpd.GetHostname().c_str());
312 wFooter->AddFDCallback(Mpd.GetFD(), StatusbarMPDCallback);
313 MessagesAllowed = 0;
314 UpdateStatusImmediately = 1;
315 # ifdef ENABLE_OUTPUTS
316 myOutputs->FetchList();
317 # endif // ENABLE_OUTPUTS
318 # ifdef ENABLE_VISUALIZER
319 myVisualizer->ResetFD();
320 if (myScreen == myVisualizer)
321 myVisualizer->SetFD();
322 # endif // ENABLE_VISUALIZER
326 TraceMpdStatus();
328 MessagesAllowed = 1;
330 // header stuff
331 gettimeofday(&past, 0);
332 if (((past.tv_sec == now.tv_sec && past.tv_usec >= now.tv_usec+500000) || past.tv_sec > now.tv_sec)
333 && (myScreen == myPlaylist || myScreen == myBrowser || myScreen == myLyrics)
336 RedrawHeader = 1;
337 gettimeofday(&now, 0);
339 if (Config.header_visibility && RedrawHeader)
341 if (title_allowed)
343 if (Config.new_design)
345 std::basic_string<my_char_t> title = myScreen->Title();
346 *wHeader << XY(0, 3) << wclrtoeol;
347 *wHeader << fmtBold << Config.alternative_ui_separator_color;
348 mvwhline(wHeader->Raw(), 2, 0, 0, COLS);
349 mvwhline(wHeader->Raw(), 4, 0, 0, COLS);
350 *wHeader << XY((COLS-Window::Length(title))/2, 3);
351 *wHeader << Config.header_color << title << clEnd;
352 *wHeader << clEnd << fmtBoldEnd;
354 else
355 *wHeader << XY(0, 0) << wclrtoeol << fmtBold << myScreen->Title() << fmtBoldEnd;
357 else
359 *wHeader << XY(0, Config.new_design ? 3 : 0)
360 << fmtBold << char(Key.Help[0]) << fmtBoldEnd << ":Help "
361 << fmtBold << char(Key.Playlist[0]) << fmtBoldEnd << ":Playlist "
362 << fmtBold << char(Key.Browser[0]) << fmtBoldEnd << ":Browse "
363 << fmtBold << char(Key.SearchEngine[0]) << fmtBoldEnd << ":Search "
364 << fmtBold << char(Key.MediaLibrary[0]) << fmtBoldEnd << ":Library "
365 << fmtBold << char(Key.PlaylistEditor[0]) << fmtBoldEnd << ":Playlist editor";
366 # ifdef HAVE_TAGLIB_H
367 *wHeader << " " << fmtBold << char(Key.TagEditor[0]) << fmtBoldEnd << ":Tag editor";
368 # endif // HAVE_TAGLIB_H
369 # ifdef ENABLE_VISUALIZER
370 *wHeader << " " << fmtBold << char(Key.Visualizer[0]) << fmtBoldEnd << ":Music visualizer";
371 # endif // ENABLE_VISUALIZER
372 # ifdef ENABLE_CLOCK
373 *wHeader << " " << fmtBold << char(Key.Clock[0]) << fmtBoldEnd << ":Clock";
374 # endif // ENABLE_CLOCK
375 if (Config.new_design)
377 *wHeader << fmtBold << Config.alternative_ui_separator_color;
378 mvwhline(wHeader->Raw(), 2, 0, 0, COLS);
379 mvwhline(wHeader->Raw(), 4, 0, 0, COLS);
380 *wHeader << clEnd << fmtBoldEnd;
383 if (!Config.new_design)
385 *wHeader << Config.volume_color;
386 *wHeader << XY(wHeader->GetWidth()-VolumeState.length(), 0) << VolumeState;
387 *wHeader << clEnd;
389 wHeader->Refresh();
390 RedrawHeader = 0;
392 // header stuff end
394 myScreen->Update();
395 if (input != ERR)
396 myScreen->RefreshWindow();
397 wFooter->ReadKey(input);
399 if (input == ERR)
400 continue;
402 if (!title_allowed)
403 RedrawHeader = 1;
404 title_allowed = 1;
406 if (myScreen == myPlaylist)
407 myPlaylist->EnableHighlighting();
408 else if (
409 myScreen == myLibrary
410 || myScreen == myPlaylistEditor
411 # ifdef HAVE_TAGLIB_H
412 || myScreen == myTagEditor
413 # endif // HAVE_TAGLIB_H
416 if (Keypressed(input, Key.Up)
417 || Keypressed(input, Key.Down)
418 || Keypressed(input, Key.PageUp)
419 || Keypressed(input, Key.PageDown)
420 || Keypressed(input, Key.Home)
421 || Keypressed(input, Key.End)
422 || Keypressed(input, Key.ApplyFilter)
423 || Keypressed(input, Key.FindForward)
424 || Keypressed(input, Key.FindBackward)
425 || Keypressed(input, Key.NextFoundPosition)
426 || Keypressed(input, Key.PrevFoundPosition))
428 if (myScreen->ActiveWindow() == myLibrary->Artists)
430 myLibrary->Albums->Clear(0);
431 myLibrary->Songs->Clear(0);
433 else if (myScreen->ActiveWindow() == myLibrary->Albums)
435 myLibrary->Songs->Clear(0);
437 else if (myScreen->ActiveWindow() == myPlaylistEditor->Playlists)
439 myPlaylistEditor->Content->Clear(0);
441 # ifdef HAVE_TAGLIB_H
442 else if (myScreen->ActiveWindow() == myTagEditor->LeftColumn)
444 myTagEditor->Tags->Clear(0);
445 myTagEditor->TagTypes->Refresh();
447 # endif // HAVE_TAGLIB_H
451 // key mapping beginning
453 if (Keypressed(input, Key.Up))
455 myScreen->Scroll(wUp, Key.Up);
457 else if (Keypressed(input, Key.Down))
459 myScreen->Scroll(wDown, Key.Down);
461 else if (Keypressed(input, Key.PageUp))
463 myScreen->Scroll(wPageUp, Key.PageUp);
465 else if (Keypressed(input, Key.PageDown))
467 myScreen->Scroll(wPageDown, Key.PageDown);
469 else if (Keypressed(input, Key.Home))
471 myScreen->Scroll(wHome);
473 else if (Keypressed(input, Key.End))
475 myScreen->Scroll(wEnd);
477 else if (Config.mouse_support && input == KEY_MOUSE)
479 getmouse(&mouse_event);
480 if (mouse_event.bstate & BUTTON1_PRESSED
481 && mouse_event.y == LINES-(Config.statusbar_visibility ? 2 : 1)
482 ) // progressbar
484 if (!myPlaylist->isPlaying())
485 continue;
486 Mpd.Seek(Mpd.GetTotalTime()*mouse_event.x/double(COLS));
487 UpdateStatusImmediately = 1;
489 else if (mouse_event.bstate & BUTTON1_PRESSED
490 && (Config.statusbar_visibility || Config.new_design)
491 && Mpd.isPlaying()
492 && mouse_event.y == (Config.new_design ? 1 : LINES-1) && mouse_event.x < 9
493 ) // playing/paused
495 Mpd.Toggle();
496 UpdateStatusImmediately = 1;
498 else if ((mouse_event.bstate & BUTTON2_PRESSED || mouse_event.bstate & BUTTON4_PRESSED)
499 && Config.header_visibility
500 && mouse_event.y == 0 && size_t(mouse_event.x) > COLS-VolumeState.length()
501 ) // volume
503 if (mouse_event.bstate & BUTTON2_PRESSED)
504 Mpd.SetVolume(Mpd.GetVolume()-2);
505 else
506 Mpd.SetVolume(Mpd.GetVolume()+2);
508 else if (mouse_event.bstate & (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED))
509 myScreen->MouseButtonPressed(mouse_event);
511 if (Keypressed(input, Key.ToggleInterface))
513 Config.new_design = !Config.new_design;
514 Config.statusbar_visibility = Config.new_design ? 0 : real_statusbar_visibility;
515 SetWindowsDimensions(header_height, footer_start_y, footer_height);
516 UnlockProgressbar();
517 UnlockStatusbar();
518 design_changed = 1;
519 # if !defined(WIN32)
520 sighandler(SIGWINCH);
521 # else
522 sighandler(0);
523 # endif // !WIN23
525 else if (Keypressed(input, Key.GoToParentDir))
527 if (myScreen == myBrowser && myBrowser->CurrentDir() != "/")
529 myBrowser->Main()->Reset();
530 myBrowser->EnterPressed();
533 else if (Keypressed(input, Key.Enter))
535 myScreen->EnterPressed();
537 else if (Keypressed(input, Key.Space))
539 myScreen->SpacePressed();
541 else if (Keypressed(input, Key.VolumeUp))
543 if (myScreen == myLibrary && input == Key.VolumeUp[0])
545 myLibrary->NextColumn();
547 else if (myScreen == myPlaylistEditor && input == Key.VolumeUp[0])
549 myPlaylistEditor->NextColumn();
551 # ifdef HAVE_TAGLIB_H
552 else if (myScreen == myTagEditor && input == Key.VolumeUp[0])
554 myTagEditor->NextColumn();
556 # endif // HAVE_TAGLIB_H
557 else
558 Mpd.SetVolume(Mpd.GetVolume()+1);
560 else if (Keypressed(input, Key.VolumeDown))
562 if (myScreen == myLibrary && input == Key.VolumeDown[0])
564 myLibrary->PrevColumn();
566 else if (myScreen == myPlaylistEditor && input == Key.VolumeDown[0])
568 myPlaylistEditor->PrevColumn();
570 # ifdef HAVE_TAGLIB_H
571 else if (myScreen == myTagEditor && input == Key.VolumeDown[0])
573 myTagEditor->PrevColumn();
575 # endif // HAVE_TAGLIB_H
576 else
577 Mpd.SetVolume(Mpd.GetVolume()-1);
579 else if (Keypressed(input, Key.Delete))
581 if (!myPlaylist->Items->Empty() && myScreen == myPlaylist)
583 Playlist::BlockUpdate = 1;
584 if (myPlaylist->Items->hasSelected())
586 std::vector<size_t> list;
587 myPlaylist->Items->GetSelected(list);
588 Mpd.StartCommandsList();
589 for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it)
591 Mpd.DeleteID((*myPlaylist->Items)[*it].GetID());
592 myPlaylist->Items->DeleteOption(*it);
594 Mpd.CommitCommandsList();
595 myPlaylist->FixPositions(list.front());
596 ShowMessage("Selected items deleted!");
598 else
600 Playlist::BlockNowPlayingUpdate = 1;
601 wFooter->SetTimeout(50);
602 int del_counter = 0;
603 while (!myPlaylist->Items->Empty() && Keypressed(input, Key.Delete))
605 size_t id = myPlaylist->Items->Choice();
606 TraceMpdStatus();
607 Playlist::BlockUpdate = 1;
608 myPlaylist->UpdateTimer();
609 // needed for keeping proper position of now playing song.
610 if (myPlaylist->NowPlaying > int(myPlaylist->CurrentSong()->GetPosition())-del_counter)
611 --myPlaylist->NowPlaying;
612 Mpd.DeleteID(myPlaylist->CurrentSong()->GetID());
613 myPlaylist->Items->DeleteOption(id);
614 myPlaylist->Items->Refresh();
615 wFooter->ReadKey(input);
616 ++del_counter;
618 myPlaylist->FixPositions(myPlaylist->Items->Choice());
619 wFooter->SetTimeout(ncmpcpp_window_timeout);
620 Playlist::BlockNowPlayingUpdate = 0;
623 else if (
624 (myScreen == myBrowser && !myBrowser->Main()->Empty() && myBrowser->Main()->Current().type == itPlaylist)
625 || (myScreen->ActiveWindow() == myPlaylistEditor->Playlists)
628 std::string name = myScreen == myBrowser ? myBrowser->Main()->Current().name : myPlaylistEditor->Playlists->Current();
629 LockStatusbar();
630 Statusbar() << "Delete playlist \"" << Shorten(TO_WSTRING(name), COLS-28) << "\" ? [" << fmtBold << 'y' << fmtBoldEnd << '/' << fmtBold << 'n' << fmtBoldEnd << "]";
631 wFooter->Refresh();
632 input = 0;
635 TraceMpdStatus();
636 wFooter->ReadKey(input);
638 while (input != 'y' && input != 'n');
639 UnlockStatusbar();
640 if (input == 'y')
642 if (Mpd.DeletePlaylist(locale_to_utf_cpy(name)))
644 static const char msg[] = "Playlist \"%s\" deleted!";
645 ShowMessage(msg, Shorten(TO_WSTRING(name), COLS-static_strlen(msg)).c_str());
646 if (myBrowser->Main() && !myBrowser->isLocal() && myBrowser->CurrentDir() == "/")
647 myBrowser->GetDirectory("/");
650 else
651 ShowMessage("Aborted!");
652 if (myPlaylistEditor->Main()) // check if initialized
653 myPlaylistEditor->Playlists->Clear(0); // make playlists list update itself
655 # ifndef WIN32
656 else if (myScreen == myBrowser && !myBrowser->Main()->Empty() && myBrowser->Main()->Current().type != itPlaylist)
658 if (!myBrowser->isLocal())
659 CHECK_MPD_MUSIC_DIR;
661 MPD::Item &item = myBrowser->Main()->Current();
663 if (item.type == itSong && !Config.allow_physical_files_deletion)
665 ShowMessage("Deleting files is disabled by default, see man page for more details");
666 continue;
668 if (item.type == itDirectory && !Config.allow_physical_directories_deletion)
670 ShowMessage("Deleting directories is disabled by default, see man page for more details");
671 continue;
673 if (item.type == itDirectory && item.song) // parent dir
674 continue;
676 std::string name = item.type == itSong ? item.song->GetName() : item.name;
677 LockStatusbar();
678 Statusbar() << "Delete " << (item.type == itSong ? "file" : "directory") << " \"" << Shorten(TO_WSTRING(name), COLS-30) << "\" ? [" << fmtBold << 'y' << fmtBoldEnd << '/' << fmtBold << 'n' << fmtBoldEnd << "] ";
679 wFooter->Refresh();
680 input = 0;
683 TraceMpdStatus();
684 wFooter->ReadKey(input);
686 while (input != 'y' && input != 'n');
687 UnlockStatusbar();
688 if (input == 'y')
690 std::string path;
691 if (!myBrowser->isLocal())
692 path = Config.mpd_music_dir;
693 path += item.type == itSong ? item.song->GetFile() : item.name;
695 if (item.type == itDirectory)
696 myBrowser->ClearDirectory(path);
698 if (remove(path.c_str()) == 0)
700 static const char msg[] = "\"%s\" deleted!";
701 ShowMessage(msg, Shorten(TO_WSTRING(name), COLS-static_strlen(msg)).c_str());
702 if (!myBrowser->isLocal())
703 Mpd.UpdateDirectory(myBrowser->CurrentDir());
704 else
705 myBrowser->GetDirectory(myBrowser->CurrentDir());
707 else
709 static const char msg[] = "Couldn't remove \"%s\": %s";
710 ShowMessage(msg, Shorten(TO_WSTRING(name), COLS-static_strlen(msg)-25).c_str(), strerror(errno));
713 else
714 ShowMessage("Aborted!");
717 # endif // !WIN32
718 else if (myScreen->ActiveWindow() == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty())
720 if (myPlaylistEditor->Content->hasSelected())
722 std::vector<size_t> list;
723 myPlaylistEditor->Content->GetSelected(list);
724 std::string playlist = locale_to_utf_cpy(myPlaylistEditor->Playlists->Current());
725 ShowMessage("Deleting selected items...");
726 Mpd.StartCommandsList();
727 for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it)
729 Mpd.Delete(playlist, *it);
730 myPlaylistEditor->Content->DeleteOption(*it);
732 Mpd.CommitCommandsList();
733 ShowMessage("Selected items deleted from playlist \"%s\"!", myPlaylistEditor->Playlists->Current().c_str());
735 else
737 wFooter->SetTimeout(50);
738 locale_to_utf(myPlaylistEditor->Playlists->Current());
739 while (!myPlaylistEditor->Content->Empty() && Keypressed(input, Key.Delete))
741 TraceMpdStatus();
742 myPlaylist->UpdateTimer();
743 Mpd.Delete(myPlaylistEditor->Playlists->Current(), myPlaylistEditor->Content->Choice());
744 myPlaylistEditor->Content->DeleteOption(myPlaylistEditor->Content->Choice());
745 myPlaylistEditor->Content->Refresh();
746 wFooter->ReadKey(input);
748 utf_to_locale(myPlaylistEditor->Playlists->Current());
749 wFooter->SetTimeout(ncmpcpp_window_timeout);
753 else if (Keypressed(input, Key.Prev))
755 Mpd.Prev();
756 UpdateStatusImmediately = 1;
758 else if (Keypressed(input, Key.Next))
760 Mpd.Next();
761 UpdateStatusImmediately = 1;
763 else if (Keypressed(input, Key.Pause))
765 Mpd.Toggle();
766 UpdateStatusImmediately = 1;
768 else if (Keypressed(input, Key.SavePlaylist))
770 LockStatusbar();
771 Statusbar() << "Save playlist as: ";
772 std::string playlist_name = wFooter->GetString();
773 std::string real_playlist_name = locale_to_utf_cpy(playlist_name);
774 UnlockStatusbar();
775 if (playlist_name.find("/") != std::string::npos)
777 ShowMessage("Playlist name cannot contain slashes!");
778 continue;
780 if (!playlist_name.empty())
782 if (myPlaylist->Items->isFiltered())
784 Mpd.StartCommandsList();
785 for (size_t i = 0; i < myPlaylist->Items->Size(); ++i)
786 Mpd.AddToPlaylist(real_playlist_name, (*myPlaylist->Items)[i]);
787 Mpd.CommitCommandsList();
788 if (Mpd.GetErrorMessage().empty())
789 ShowMessage("Filtered items added to playlist \"%s\"", playlist_name.c_str());
791 else if (Mpd.SavePlaylist(real_playlist_name))
793 ShowMessage("Playlist saved as: %s", playlist_name.c_str());
794 if (myPlaylistEditor->Main()) // check if initialized
795 myPlaylistEditor->Playlists->Clear(0); // make playlist's list update itself
797 else
799 LockStatusbar();
800 Statusbar() << "Playlist already exists, overwrite: " << playlist_name << " ? [" << fmtBold << 'y' << fmtBoldEnd << '/' << fmtBold << 'n' << fmtBoldEnd << "] ";
801 wFooter->Refresh();
802 input = 0;
803 while (input != 'y' && input != 'n')
805 TraceMpdStatus();
806 wFooter->ReadKey(input);
808 UnlockStatusbar();
810 if (input == 'y')
812 Mpd.DeletePlaylist(real_playlist_name);
813 if (Mpd.SavePlaylist(real_playlist_name))
814 ShowMessage("Playlist overwritten!");
816 else
817 ShowMessage("Aborted!");
818 if (myPlaylistEditor->Main()) // check if initialized
819 myPlaylistEditor->Playlists->Clear(0); // make playlist's list update itself
820 if (myScreen == myPlaylist)
821 myPlaylist->EnableHighlighting();
824 if (myBrowser->Main()
825 && !myBrowser->isLocal()
826 && myBrowser->CurrentDir() == "/"
827 && !myBrowser->Main()->Empty())
828 myBrowser->GetDirectory(myBrowser->CurrentDir());
830 else if (Keypressed(input, Key.Stop))
832 Mpd.Stop();
833 UpdateStatusImmediately = 1;
835 else if (Keypressed(input, Key.MvSongUp))
837 if (myScreen == myPlaylist && myPlaylist->SortingInProgress())
838 myPlaylist->AdjustSortOrder(input);
839 else if (myScreen == myPlaylist && !myPlaylist->Items->Empty())
841 CHECK_PLAYLIST_FOR_FILTERING;
842 wFooter->SetTimeout(50);
843 if (myPlaylist->Items->hasSelected())
845 std::vector<size_t> list;
846 myPlaylist->Items->GetSelected(list);
847 std::vector<size_t> origs(list);
849 // NOTICE: since ncmpcpp only pretends to move the songs until the key is
850 // released, mpd doesn't know about the change while the songs are moved
851 // so wee need to block playlist update for this time and also if one of
852 // the songs being moved is currently playing, now playing update to prevent
853 // mpd from 'updating' and thus showing wrong position
855 bool modify_now_playing = 0;
856 for (std::vector<size_t>::iterator it = list.begin(); it != list.end(); ++it)
858 if (*it == size_t(myPlaylist->NowPlaying) && list.front() > 0)
860 modify_now_playing = 1;
861 Playlist::BlockNowPlayingUpdate = 1;
862 break;
866 while (Keypressed(input, Key.MvSongUp) && list.front() > 0)
868 TraceMpdStatus();
869 Playlist::BlockUpdate = 1;
870 myPlaylist->UpdateTimer();
871 if (modify_now_playing)
872 --myPlaylist->NowPlaying;
873 for (std::vector<size_t>::iterator it = list.begin(); it != list.end(); ++it)
875 --*it;
876 myPlaylist->Items->at((*it)+1).SetPosition(*it);
877 myPlaylist->Items->at(*it).SetPosition((*it)+1);
878 myPlaylist->Items->Swap(*it, (*it)+1);
880 myPlaylist->Items->Highlight(list[(list.size()-1)/2]);
881 myPlaylist->Items->Refresh();
882 wFooter->ReadKey(input);
884 Playlist::BlockNowPlayingUpdate = 0;
885 Mpd.StartCommandsList();
886 for (size_t i = 0; i < list.size(); ++i)
887 Mpd.Move(origs[i], list[i]);
888 Mpd.CommitCommandsList();
890 else
892 size_t from, to;
893 from = to = myPlaylist->Items->Choice();
894 bool modify_now_playing = from == size_t(myPlaylist->NowPlaying);
895 if (modify_now_playing)
896 Playlist::BlockNowPlayingUpdate = 1;
897 while (Keypressed(input, Key.MvSongUp) && to > 0)
899 TraceMpdStatus();
900 Playlist::BlockUpdate = 1;
901 myPlaylist->UpdateTimer();
902 if (modify_now_playing)
903 --myPlaylist->NowPlaying;
904 --to;
905 myPlaylist->Items->at(from).SetPosition(to);
906 myPlaylist->Items->at(to).SetPosition(from);
907 myPlaylist->Items->Swap(to, to+1);
908 myPlaylist->Items->Scroll(wUp);
909 myPlaylist->Items->Refresh();
910 wFooter->ReadKey(input);
912 Mpd.Move(from, to);
913 Playlist::BlockNowPlayingUpdate = 0;
914 UpdateStatusImmediately = 1;
916 wFooter->SetTimeout(ncmpcpp_window_timeout);
918 else if (myScreen->ActiveWindow() == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty())
920 wFooter->SetTimeout(50);
921 if (myPlaylistEditor->Content->hasSelected())
923 std::vector<size_t> list;
924 myPlaylistEditor->Content->GetSelected(list);
926 std::vector<size_t> origs(list);
928 while (Keypressed(input, Key.MvSongUp) && list.front() > 0)
930 TraceMpdStatus();
931 myPlaylist->UpdateTimer();
932 for (std::vector<size_t>::iterator it = list.begin(); it != list.end(); ++it)
934 --*it;
935 myPlaylistEditor->Content->Swap(*it, (*it)+1);
937 myPlaylistEditor->Content->Highlight(list[(list.size()-1)/2]);
938 myPlaylistEditor->Content->Refresh();
939 wFooter->ReadKey(input);
941 Mpd.StartCommandsList();
942 for (size_t i = 0; i < list.size(); ++i)
943 if (origs[i] != list[i])
944 Mpd.Move(myPlaylistEditor->Playlists->Current(), origs[i], list[i]);
945 Mpd.CommitCommandsList();
947 else
949 size_t from, to;
950 from = to = myPlaylistEditor->Content->Choice();
951 while (Keypressed(input, Key.MvSongUp) && to > 0)
953 TraceMpdStatus();
954 myPlaylist->UpdateTimer();
955 --to;
956 myPlaylistEditor->Content->Swap(to, to+1);
957 myPlaylistEditor->Content->Scroll(wUp);
958 myPlaylistEditor->Content->Refresh();
959 wFooter->ReadKey(input);
961 if (from != to)
962 Mpd.Move(myPlaylistEditor->Playlists->Current(), from, to);
964 wFooter->SetTimeout(ncmpcpp_window_timeout);
967 else if (Keypressed(input, Key.MvSongDown))
969 if (myScreen == myPlaylist && myPlaylist->SortingInProgress())
970 myPlaylist->AdjustSortOrder(input);
971 else if (myScreen == myPlaylist && !myPlaylist->Items->Empty())
973 CHECK_PLAYLIST_FOR_FILTERING;
974 wFooter->SetTimeout(50);
975 if (myPlaylist->Items->hasSelected())
977 std::vector<size_t> list;
978 myPlaylist->Items->GetSelected(list);
979 std::vector<size_t> origs(list);
981 bool modify_now_playing = 0;
982 for (std::vector<size_t>::iterator it = list.begin(); it != list.end(); ++it)
984 if (*it == size_t(myPlaylist->NowPlaying) && list.back() < myPlaylist->Items->Size()-1)
986 modify_now_playing = 1;
987 Playlist::BlockNowPlayingUpdate = 1;
988 break;
992 while (Keypressed(input, Key.MvSongDown) && list.back() < myPlaylist->Items->Size()-1)
994 TraceMpdStatus();
995 Playlist::BlockUpdate = 1;
996 myPlaylist->UpdateTimer();
997 if (modify_now_playing)
998 ++myPlaylist->NowPlaying;
999 for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it)
1001 ++*it;
1002 myPlaylist->Items->at((*it)-1).SetPosition(*it);
1003 myPlaylist->Items->at(*it).SetPosition((*it)-1);
1004 myPlaylist->Items->Swap(*it, (*it)-1);
1006 myPlaylist->Items->Highlight(list[(list.size()-1)/2]);
1007 myPlaylist->Items->Refresh();
1008 wFooter->ReadKey(input);
1010 Playlist::BlockNowPlayingUpdate = 0;
1011 Mpd.StartCommandsList();
1012 for (int i = list.size()-1; i >= 0; --i)
1013 Mpd.Move(origs[i], list[i]);
1014 Mpd.CommitCommandsList();
1016 else
1018 size_t from, to;
1019 from = to = myPlaylist->Items->Choice();
1020 bool modify_now_playing = from == size_t(myPlaylist->NowPlaying);
1021 if (modify_now_playing)
1022 Playlist::BlockNowPlayingUpdate = 1;
1023 while (Keypressed(input, Key.MvSongDown) && to < myPlaylist->Items->Size()-1)
1025 TraceMpdStatus();
1026 Playlist::BlockUpdate = 1;
1027 myPlaylist->UpdateTimer();
1028 if (modify_now_playing)
1029 ++myPlaylist->NowPlaying;
1030 ++to;
1031 myPlaylist->Items->at(from).SetPosition(to);
1032 myPlaylist->Items->at(to).SetPosition(from);
1033 myPlaylist->Items->Swap(to, to-1);
1034 myPlaylist->Items->Scroll(wDown);
1035 myPlaylist->Items->Refresh();
1036 wFooter->ReadKey(input);
1038 Mpd.Move(from, to);
1039 Playlist::BlockNowPlayingUpdate = 0;
1040 UpdateStatusImmediately = 1;
1042 wFooter->SetTimeout(ncmpcpp_window_timeout);
1045 else if (myScreen->ActiveWindow() == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty())
1047 wFooter->SetTimeout(50);
1048 if (myPlaylistEditor->Content->hasSelected())
1050 std::vector<size_t> list;
1051 myPlaylistEditor->Content->GetSelected(list);
1053 std::vector<size_t> origs(list);
1055 while (Keypressed(input, Key.MvSongDown) && list.back() < myPlaylistEditor->Content->Size()-1)
1057 TraceMpdStatus();
1058 myPlaylist->UpdateTimer();
1059 for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it)
1061 ++*it;
1062 myPlaylistEditor->Content->Swap(*it, (*it)-1);
1064 myPlaylistEditor->Content->Highlight(list[(list.size()-1)/2]);
1065 myPlaylistEditor->Content->Refresh();
1066 wFooter->ReadKey(input);
1068 Mpd.StartCommandsList();
1069 for (int i = list.size()-1; i >= 0; --i)
1070 if (origs[i] != list[i])
1071 Mpd.Move(myPlaylistEditor->Playlists->Current(), origs[i], list[i]);
1072 Mpd.CommitCommandsList();
1074 else
1076 size_t from, to;
1077 from = to = myPlaylistEditor->Content->Choice();
1078 while (Keypressed(input, Key.MvSongDown) && to < myPlaylistEditor->Content->Size()-1)
1080 TraceMpdStatus();
1081 myPlaylist->UpdateTimer();
1082 ++to;
1083 myPlaylistEditor->Content->Swap(to, to-1);
1084 myPlaylistEditor->Content->Scroll(wDown);
1085 myPlaylistEditor->Content->Refresh();
1086 wFooter->ReadKey(input);
1088 if (from != to)
1089 Mpd.Move(myPlaylistEditor->Playlists->Current(), from, to);
1091 wFooter->SetTimeout(ncmpcpp_window_timeout);
1094 else if (Keypressed(input, Key.MoveTo) && myScreen == myPlaylist)
1096 CHECK_PLAYLIST_FOR_FILTERING;
1097 if (!myPlaylist->Items->hasSelected())
1099 ShowMessage("No selected items to move!");
1100 continue;
1102 Playlist::BlockUpdate = 1;
1103 size_t pos = myPlaylist->Items->Choice();
1104 // if cursor is at the last item, break convention and move at the end of playlist
1105 if (pos == myPlaylist->Items->Size()-1)
1106 pos++;
1107 std::vector<size_t> list;
1108 myPlaylist->Items->GetSelected(list);
1109 if (pos >= list.front() && pos <= list.back())
1110 continue;
1111 int diff = pos-list.front();
1112 Mpd.StartCommandsList();
1113 if (diff > 0)
1115 pos -= list.size();
1116 size_t i = list.size()-1;
1117 for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it, --i)
1119 Mpd.Move(*it, pos+i);
1120 myPlaylist->Items->Move(*it, pos+i);
1123 else if (diff < 0)
1125 size_t i = 0;
1126 for (std::vector<size_t>::const_iterator it = list.begin(); it != list.end(); ++it, ++i)
1128 Mpd.Move(*it, pos+i);
1129 myPlaylist->Items->Move(*it, pos+i);
1132 Mpd.CommitCommandsList();
1133 myPlaylist->Items->Highlight(pos);
1134 myPlaylist->FixPositions();
1136 else if (Keypressed(input, Key.Add))
1138 if (myScreen == myPlaylistEditor && myPlaylistEditor->Playlists->Empty())
1139 continue;
1140 LockStatusbar();
1141 Statusbar() << (myScreen == myPlaylistEditor ? "Add to playlist: " : "Add: ");
1142 std::string path = wFooter->GetString();
1143 UnlockStatusbar();
1144 if (!path.empty())
1146 if (myScreen == myPlaylistEditor)
1148 Mpd.AddToPlaylist(myPlaylistEditor->Playlists->Current(), path);
1149 myPlaylistEditor->Content->Clear(0); // make it refetch content of playlist
1151 else
1152 Mpd.Add(path);
1153 UpdateStatusImmediately = 1;
1156 else if (Keypressed(input, Key.SeekForward) || Keypressed(input, Key.SeekBackward))
1158 if (!Mpd.GetTotalTime())
1160 ShowMessage("Unknown item length!");
1161 continue;
1164 const Song *s = myPlaylist->NowPlayingSong();
1165 if (!s)
1166 continue;
1168 LockProgressbar();
1169 LockStatusbar();
1171 int songpos;
1172 time_t t = time(0);
1174 songpos = Mpd.GetElapsedTime();
1176 SeekingInProgress = 1;
1177 *wFooter << fmtBold;
1178 while (Keypressed(input, Key.SeekForward) || Keypressed(input, Key.SeekBackward))
1180 TraceMpdStatus();
1181 myPlaylist->UpdateTimer();
1182 wFooter->ReadKey(input);
1184 int howmuch = Config.incremental_seeking ? (myPlaylist->Timer()-t)/2+Config.seek_time : Config.seek_time;
1186 if (Keypressed(input, Key.SeekForward) && songpos < Mpd.GetTotalTime())
1188 songpos += howmuch;
1189 if (songpos > Mpd.GetTotalTime())
1190 songpos = Mpd.GetTotalTime();
1192 else if (Keypressed(input, Key.SeekBackward) && songpos > 0)
1194 songpos -= howmuch;
1195 if (songpos < 0)
1196 songpos = 0;
1199 std::string tracklength;
1200 if (Config.new_design)
1202 if (Config.display_remaining_time)
1204 tracklength = "-";
1205 tracklength += Song::ShowTime(Mpd.GetTotalTime()-songpos);
1207 else
1208 tracklength = Song::ShowTime(songpos);
1209 tracklength += "/";
1210 tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime());
1211 *wHeader << XY(0, 0) << tracklength << " ";
1212 wHeader->Refresh();
1214 else
1216 tracklength = " [";
1217 if (Config.display_remaining_time)
1219 tracklength += "-";
1220 tracklength += Song::ShowTime(Mpd.GetTotalTime()-songpos);
1222 else
1223 tracklength += Song::ShowTime(songpos);
1224 tracklength += "/";
1225 tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime());
1226 tracklength += "]";
1227 *wFooter << XY(wFooter->GetWidth()-tracklength.length(), 1) << tracklength;
1229 DrawProgressbar(songpos, Mpd.GetTotalTime());
1230 wFooter->Refresh();
1232 *wFooter << fmtBoldEnd;
1233 SeekingInProgress = 0;
1234 Mpd.Seek(songpos);
1235 UpdateStatusImmediately = 1;
1237 UnlockProgressbar();
1238 UnlockStatusbar();
1240 else if (Keypressed(input, Key.ToggleDisplayMode))
1242 if (myScreen == myPlaylist)
1244 Config.columns_in_playlist = !Config.columns_in_playlist;
1245 ShowMessage("Playlist display mode: %s", Config.columns_in_playlist ? "Columns" : "Classic");
1247 if (Config.columns_in_playlist)
1249 myPlaylist->Items->SetItemDisplayer(Display::SongsInColumns);
1250 myPlaylist->Items->SetTitle(Display::Columns());
1251 myPlaylist->Items->SetGetStringFunction(Playlist::SongInColumnsToString);
1253 else
1255 myPlaylist->Items->SetItemDisplayer(Display::Songs);
1256 myPlaylist->Items->SetTitle("");
1257 myPlaylist->Items->SetGetStringFunction(Playlist::SongToString);
1260 else if (myScreen == myBrowser)
1262 Config.columns_in_browser = !Config.columns_in_browser;
1263 ShowMessage("Browser display mode: %s", Config.columns_in_browser ? "Columns" : "Classic");
1264 myBrowser->Main()->SetTitle(Config.columns_in_browser ? Display::Columns() : "");
1267 else if (myScreen == mySearcher)
1269 Config.columns_in_search_engine = !Config.columns_in_search_engine;
1270 ShowMessage("Search engine display mode: %s", Config.columns_in_search_engine ? "Columns" : "Classic");
1271 if (mySearcher->Main()->Size() > SearchEngine::StaticOptions)
1272 mySearcher->Main()->SetTitle(Config.columns_in_search_engine ? Display::Columns() : "");
1275 # ifdef HAVE_CURL_CURL_H
1276 else if (Keypressed(input, Key.ToggleLyricsDB))
1278 const char *current_lyrics_plugin = Lyrics::GetPluginName(++Config.lyrics_db);
1279 if (!current_lyrics_plugin)
1281 current_lyrics_plugin = Lyrics::GetPluginName(Config.lyrics_db = 0);
1283 ShowMessage("Using lyrics database: %s", current_lyrics_plugin);
1285 # endif // HAVE_CURL_CURL_H
1286 else if (Keypressed(input, Key.ToggleAutoCenter))
1288 Config.autocenter_mode = !Config.autocenter_mode;
1289 ShowMessage("Auto center mode: %s", Config.autocenter_mode ? "On" : "Off");
1290 if (Config.autocenter_mode && myPlaylist->isPlaying() && !myPlaylist->Items->isFiltered())
1291 myPlaylist->Items->Highlight(myPlaylist->NowPlaying);
1293 else if (Keypressed(input, Key.UpdateDB))
1295 if (myScreen == myBrowser)
1296 Mpd.UpdateDirectory(locale_to_utf_cpy(myBrowser->CurrentDir()));
1297 # ifdef HAVE_TAGLIB_H
1298 else if (myScreen == myTagEditor && !Config.albums_in_tag_editor)
1299 Mpd.UpdateDirectory(myTagEditor->CurrentDir());
1300 # endif // HAVE_TAGLIB_H
1301 else
1302 Mpd.UpdateDirectory("/");
1304 else if (Keypressed(input, Key.GoToNowPlaying))
1306 if (myScreen == myPlaylist && myPlaylist->isPlaying())
1308 CHECK_PLAYLIST_FOR_FILTERING;
1309 myPlaylist->Items->Highlight(myPlaylist->NowPlaying);
1311 else if (myScreen == myBrowser)
1313 if (const Song *s = myPlaylist->NowPlayingSong())
1315 myBrowser->LocateSong(*s);
1316 RedrawHeader = 1;
1320 else if (Keypressed(input, Key.ToggleRepeat))
1322 Mpd.SetRepeat(!Mpd.GetRepeat());
1323 UpdateStatusImmediately = 1;
1325 else if (Keypressed(input, Key.Shuffle))
1327 Mpd.Shuffle();
1328 UpdateStatusImmediately = 1;
1330 else if (Keypressed(input, Key.ToggleRandom))
1332 Mpd.SetRandom(!Mpd.GetRandom());
1333 UpdateStatusImmediately = 1;
1335 else if (Keypressed(input, Key.ToggleSingle))
1337 if (myScreen == mySearcher && !mySearcher->Main()->isStatic(0))
1339 mySearcher->Main()->Highlight(SearchEngine::SearchButton);
1340 mySearcher->Main()->Highlighting(0);
1341 mySearcher->Main()->Refresh();
1342 mySearcher->Main()->Highlighting(1);
1343 mySearcher->EnterPressed();
1345 # ifdef HAVE_TAGLIB_H
1346 else if (myScreen == myTinyTagEditor)
1348 myTinyTagEditor->Main()->Highlight(myTinyTagEditor->Main()->Size()-2); // Save
1349 myTinyTagEditor->EnterPressed();
1351 # endif // HAVE_TAGLIB_H
1352 else
1354 Mpd.SetSingle(!Mpd.GetSingle());
1355 UpdateStatusImmediately = 1;
1358 else if (Keypressed(input, Key.ToggleConsume))
1360 Mpd.SetConsume(!Mpd.GetConsume());
1361 UpdateStatusImmediately = 1;
1363 else if (Keypressed(input, Key.ToggleCrossfade))
1365 Mpd.SetCrossfade(Mpd.GetCrossfade() ? 0 : Config.crossfade_time);
1366 UpdateStatusImmediately = 1;
1368 else if (Keypressed(input, Key.SetCrossfade))
1370 LockStatusbar();
1371 Statusbar() << "Set crossfade to: ";
1372 std::string crossfade = wFooter->GetString(3);
1373 UnlockStatusbar();
1374 int cf = StrToInt(crossfade);
1375 if (cf > 0)
1377 Config.crossfade_time = cf;
1378 Mpd.SetCrossfade(cf);
1379 UpdateStatusImmediately = 1;
1382 else if (Keypressed(input, Key.EditTags))
1384 if ((myScreen != myBrowser || !myBrowser->isLocal())
1385 && myScreen != myLyrics)
1386 CHECK_MPD_MUSIC_DIR;
1387 # ifdef HAVE_TAGLIB_H
1388 if (myTinyTagEditor->SetEdited(myScreen->CurrentSong()))
1390 myTinyTagEditor->SwitchTo();
1392 else if (myScreen->ActiveWindow() == myLibrary->Artists)
1394 LockStatusbar();
1395 Statusbar() << fmtBold << IntoStr(Config.media_lib_primary_tag) << fmtBoldEnd << ": ";
1396 std::string new_tag = wFooter->GetString(myLibrary->Artists->Current());
1397 UnlockStatusbar();
1398 if (!new_tag.empty() && new_tag != myLibrary->Artists->Current())
1400 bool success = 1;
1401 SongList list;
1402 ShowMessage("Updating tags...");
1403 Mpd.StartSearch(1);
1404 Mpd.AddSearch(Config.media_lib_primary_tag, locale_to_utf_cpy(myLibrary->Artists->Current()));
1405 Mpd.CommitSearch(list);
1406 Song::SetFunction set = IntoSetFunction(Config.media_lib_primary_tag);
1407 if (!set)
1408 continue;
1409 for (SongList::iterator it = list.begin(); it != list.end(); ++it)
1411 (*it)->Localize();
1412 (*it)->SetTags(set, new_tag);
1413 ShowMessage("Updating tags in \"%s\"...", (*it)->GetName().c_str());
1414 std::string path = Config.mpd_music_dir + (*it)->GetFile();
1415 if (!TagEditor::WriteTags(**it))
1417 static const char msg[] = "Error while updating tags in \"%s\"!";
1418 ShowMessage(msg, Shorten(TO_WSTRING((*it)->GetFile()), COLS-static_strlen(msg)).c_str());
1419 success = 0;
1420 break;
1423 if (success)
1425 Mpd.UpdateDirectory(locale_to_utf_cpy(FindSharedDir(list)));
1426 ShowMessage("Tags updated succesfully!");
1428 FreeSongList(list);
1431 else if (myScreen->ActiveWindow() == myLibrary->Albums)
1433 LockStatusbar();
1434 Statusbar() << fmtBold << "Album: " << fmtBoldEnd;
1435 std::string new_album = wFooter->GetString(myLibrary->Albums->Current().second.Album);
1436 UnlockStatusbar();
1437 if (!new_album.empty() && new_album != myLibrary->Albums->Current().second.Album)
1439 bool success = 1;
1440 ShowMessage("Updating tags...");
1441 for (size_t i = 0; i < myLibrary->Songs->Size(); ++i)
1443 (*myLibrary->Songs)[i].Localize();
1444 ShowMessage("Updating tags in \"%s\"...", (*myLibrary->Songs)[i].GetName().c_str());
1445 std::string path = Config.mpd_music_dir + (*myLibrary->Songs)[i].GetFile();
1446 TagLib::FileRef f(locale_to_utf_cpy(path).c_str());
1447 if (f.isNull())
1449 static const char msg[] = "Error while opening file \"%s\"!";
1450 ShowMessage(msg, Shorten(TO_WSTRING((*myLibrary->Songs)[i].GetFile()), COLS-static_strlen(msg)).c_str());
1451 success = 0;
1452 break;
1454 f.tag()->setAlbum(ToWString(new_album));
1455 if (!f.save())
1457 static const char msg[] = "Error while writing tags in \"%s\"!";
1458 ShowMessage(msg, Shorten(TO_WSTRING((*myLibrary->Songs)[i].GetFile()), COLS-static_strlen(msg)).c_str());
1459 success = 0;
1460 break;
1463 if (success)
1465 Mpd.UpdateDirectory(locale_to_utf_cpy(FindSharedDir(myLibrary->Songs)));
1466 ShowMessage("Tags updated succesfully!");
1470 else if (myScreen->ActiveWindow() == myTagEditor->Dirs)
1472 std::string old_dir = myTagEditor->Dirs->Current().first;
1473 LockStatusbar();
1474 Statusbar() << fmtBold << "Directory: " << fmtBoldEnd;
1475 std::string new_dir = wFooter->GetString(old_dir);
1476 UnlockStatusbar();
1477 if (!new_dir.empty() && new_dir != old_dir)
1479 std::string full_old_dir = Config.mpd_music_dir + myTagEditor->CurrentDir() + "/" + locale_to_utf_cpy(old_dir);
1480 std::string full_new_dir = Config.mpd_music_dir + myTagEditor->CurrentDir() + "/" + locale_to_utf_cpy(new_dir);
1481 if (rename(full_old_dir.c_str(), full_new_dir.c_str()) == 0)
1483 static const char msg[] = "Directory renamed to \"%s\"";
1484 ShowMessage(msg, Shorten(TO_WSTRING(new_dir), COLS-static_strlen(msg)).c_str());
1485 Mpd.UpdateDirectory(myTagEditor->CurrentDir());
1487 else
1489 static const char msg[] = "Couldn't rename \"%s\": %s";
1490 ShowMessage(msg, Shorten(TO_WSTRING(old_dir), COLS-static_strlen(msg)-25).c_str(), strerror(errno));
1494 else
1495 # endif // HAVE_TAGLIB_H
1496 if (myScreen == myLyrics)
1498 myLyrics->Edit();
1500 if (myScreen == myBrowser && myBrowser->Main()->Current().type == itDirectory)
1502 std::string old_dir = myBrowser->Main()->Current().name;
1503 LockStatusbar();
1504 Statusbar() << fmtBold << "Directory: " << fmtBoldEnd;
1505 std::string new_dir = wFooter->GetString(old_dir);
1506 UnlockStatusbar();
1507 if (!new_dir.empty() && new_dir != old_dir)
1509 std::string full_old_dir;
1510 if (!myBrowser->isLocal())
1511 full_old_dir += Config.mpd_music_dir;
1512 full_old_dir += locale_to_utf_cpy(old_dir);
1513 std::string full_new_dir;
1514 if (!myBrowser->isLocal())
1515 full_new_dir += Config.mpd_music_dir;
1516 full_new_dir += locale_to_utf_cpy(new_dir);
1517 int rename_result = rename(full_old_dir.c_str(), full_new_dir.c_str());
1518 if (rename_result == 0)
1520 static const char msg[] = "Directory renamed to \"%s\"";
1521 ShowMessage(msg, Shorten(TO_WSTRING(new_dir), COLS-static_strlen(msg)).c_str());
1522 if (!myBrowser->isLocal())
1523 Mpd.UpdateDirectory(locale_to_utf_cpy(FindSharedDir(old_dir, new_dir)));
1524 myBrowser->GetDirectory(myBrowser->CurrentDir());
1526 else
1528 static const char msg[] = "Couldn't rename \"%s\": %s";
1529 ShowMessage(msg, Shorten(TO_WSTRING(old_dir), COLS-static_strlen(msg)-25).c_str(), strerror(errno));
1533 else if (myScreen->ActiveWindow() == myPlaylistEditor->Playlists || (myScreen == myBrowser && myBrowser->Main()->Current().type == itPlaylist))
1535 std::string old_name = myScreen->ActiveWindow() == myPlaylistEditor->Playlists ? myPlaylistEditor->Playlists->Current() : myBrowser->Main()->Current().name;
1536 LockStatusbar();
1537 Statusbar() << fmtBold << "Playlist: " << fmtBoldEnd;
1538 std::string new_name = wFooter->GetString(old_name);
1539 UnlockStatusbar();
1540 if (!new_name.empty() && new_name != old_name)
1542 if (Mpd.Rename(locale_to_utf_cpy(old_name), locale_to_utf_cpy(new_name)))
1544 static const char msg[] = "Playlist renamed to \"%s\"";
1545 ShowMessage(msg, Shorten(TO_WSTRING(new_name), COLS-static_strlen(msg)).c_str());
1546 if (myBrowser->Main() && !myBrowser->isLocal())
1547 myBrowser->GetDirectory("/");
1548 if (myPlaylistEditor->Main())
1549 myPlaylistEditor->Playlists->Clear(0);
1554 else if (Keypressed(input, Key.GoToContainingDir))
1556 Song *s = myScreen->CurrentSong();
1557 if (s)
1558 myBrowser->LocateSong(*s);
1560 else if (Keypressed(input, Key.GoToPosition))
1562 if (!Mpd.GetTotalTime())
1564 ShowMessage("Unknown item length!");
1565 continue;
1568 const Song *s = myPlaylist->NowPlayingSong();
1569 if (!s)
1570 continue;
1572 LockStatusbar();
1573 Statusbar() << "Position to go (in %/mm:ss/seconds(s)): ";
1574 std::string position = wFooter->GetString();
1575 UnlockStatusbar();
1577 if (position.empty())
1578 continue;
1580 int newpos = 0;
1581 if (position.find(':') != std::string::npos) // probably time in mm:ss
1583 newpos = StrToInt(position)*60 + StrToInt(position.substr(position.find(':')+1));
1584 if (newpos >= 0 && newpos <= Mpd.GetTotalTime())
1585 Mpd.Seek(newpos);
1586 else
1587 ShowMessage("Out of bounds, 0:00-%s possible for mm:ss, %s given.", s->GetLength().c_str(), MPD::Song::ShowTime(newpos).c_str());
1589 else if (position.find('s') != std::string::npos) // probably position in seconds
1591 newpos = StrToInt(position);
1592 if (newpos >= 0 && newpos <= Mpd.GetTotalTime())
1593 Mpd.Seek(newpos);
1594 else
1595 ShowMessage("Out of bounds, 0-%d possible for seconds, %d given.", s->GetTotalLength(), newpos);
1597 else
1599 newpos = StrToInt(position);
1600 if (newpos >= 0 && newpos <= 100)
1601 Mpd.Seek(Mpd.GetTotalTime()*newpos/100.0);
1602 else
1603 ShowMessage("Out of bounds, 0-100 possible for %%, %d given.", newpos);
1605 UpdateStatusImmediately = 1;
1607 else if (Keypressed(input, Key.ReverseSelection))
1609 if (myScreen->allowsSelection())
1611 myScreen->ReverseSelection();
1612 ShowMessage("Selection reversed!");
1615 else if (Keypressed(input, Key.DeselectAll))
1617 if (myScreen->allowsSelection())
1619 List *mList = myScreen->GetList();
1620 if (!mList->hasSelected())
1621 continue;
1622 for (size_t i = 0; i < mList->Size(); ++i)
1623 mList->Select(i, 0);
1624 ShowMessage("Items deselected!");
1627 else if (Keypressed(input, Key.AddSelected))
1629 mySelectedItemsAdder->SwitchTo();
1631 else if (Keypressed(input, Key.Crop))
1633 CHECK_PLAYLIST_FOR_FILTERING;
1634 if (myPlaylist->Items->hasSelected())
1636 Mpd.StartCommandsList();
1637 for (int i = myPlaylist->Items->Size()-1; i >= 0; --i)
1639 if (!myPlaylist->Items->isSelected(i) && i != myPlaylist->NowPlaying)
1640 Mpd.Delete(i);
1642 // if mpd deletes now playing song deletion will be sluggishly slow
1643 // then so we have to assure it will be deleted at the very end.
1644 if (myPlaylist->isPlaying() && !myPlaylist->Items->isSelected(myPlaylist->NowPlaying))
1645 Mpd.DeleteID(myPlaylist->NowPlayingSong()->GetID());
1647 ShowMessage("Deleting all items but selected...");
1648 Mpd.CommitCommandsList();
1649 ShowMessage("Items deleted!");
1651 else
1653 if (!myPlaylist->isPlaying())
1655 ShowMessage("Nothing is playing now!");
1656 continue;
1658 Mpd.StartCommandsList();
1659 for (int i = myPlaylist->Items->Size()-1; i >= 0; --i)
1660 if (i != myPlaylist->NowPlaying)
1661 Mpd.Delete(i);
1662 ShowMessage("Deleting all items except now playing one...");
1663 Mpd.CommitCommandsList();
1664 ShowMessage("Items deleted!");
1667 else if (Keypressed(input, Key.Clear))
1669 if (myScreen == myPlaylistEditor && myPlaylistEditor->Playlists->Empty())
1670 continue;
1672 if (myScreen->ActiveWindow() == myPlaylistEditor->Content
1673 || Config.ask_before_clearing_main_playlist)
1675 LockStatusbar();
1676 Statusbar() << "Do you really want to clear playlist";
1677 if (myScreen->ActiveWindow() == myPlaylistEditor->Content)
1678 *wFooter << " \"" << myPlaylistEditor->Playlists->Current() << "\"";
1679 *wFooter << " ? [" << fmtBold << 'y' << fmtBoldEnd << '/' << fmtBold << 'n' << fmtBoldEnd << "] ";
1680 wFooter->Refresh();
1681 input = 0;
1684 TraceMpdStatus();
1685 wFooter->ReadKey(input);
1687 while (input != 'y' && input != 'n');
1688 UnlockStatusbar();
1689 if (input != 'y')
1691 ShowMessage("Aborted!");
1692 continue;
1696 if (myPlaylist->Items->isFiltered())
1698 ShowMessage("Deleting filtered items...");
1699 Mpd.StartCommandsList();
1700 for (int i = myPlaylist->Items->Size()-1; i >= 0; --i)
1701 Mpd.Delete((*myPlaylist->Items)[i].GetPosition());
1702 Mpd.CommitCommandsList();
1703 ShowMessage("Filtered items deleted!");
1705 else
1707 if (myScreen->ActiveWindow() == myPlaylistEditor->Content)
1709 Mpd.ClearPlaylist(locale_to_utf_cpy(myPlaylistEditor->Playlists->Current()));
1710 myPlaylistEditor->Content->Clear(0);
1712 else
1714 ShowMessage("Clearing playlist...");
1715 Mpd.ClearPlaylist();
1718 // if playlist is cleared, items list have to be updated, but this
1719 // can be blocked if new song was added to playlist less than one
1720 // second ago, so we need to assume it's unlocked.
1721 BlockItemListUpdate = 0;
1722 UpdateStatusImmediately = 1;
1724 else if (Keypressed(input, Key.SortPlaylist) && myScreen == myPlaylist)
1726 CHECK_PLAYLIST_FOR_FILTERING;
1727 myPlaylist->Sort();
1729 else if (Keypressed(input, Key.ApplyFilter))
1731 List *mList = myScreen->GetList();
1733 if (!mList)
1734 continue;
1736 LockStatusbar();
1737 Statusbar() << fmtBold << "Apply filter: " << fmtBoldEnd;
1738 wFooter->SetGetStringHelper(StatusbarApplyFilterImmediately);
1739 wFooter->GetString(mList->GetFilter());
1740 wFooter->SetGetStringHelper(StatusbarGetStringHelper);
1741 UnlockStatusbar();
1743 if (mList->isFiltered())
1744 ShowMessage("Using filter \"%s\"", mList->GetFilter().c_str());
1745 else
1746 ShowMessage("Filtering disabled");
1748 if (myScreen == myPlaylist)
1750 myPlaylist->EnableHighlighting();
1751 Playlist::ReloadTotalLength = 1;
1752 RedrawHeader = 1;
1755 else if (Keypressed(input, Key.FindForward) || Keypressed(input, Key.FindBackward))
1757 List *mList = myScreen->GetList();
1759 if (mList)
1761 LockStatusbar();
1762 Statusbar() << "Find " << (Keypressed(input, Key.FindForward) ? "forward" : "backward") << ": ";
1763 std::string findme = wFooter->GetString();
1764 UnlockStatusbar();
1765 myPlaylist->UpdateTimer();
1767 if (!findme.empty())
1768 ShowMessage("Searching...");
1770 bool success = mList->Search(findme, myScreen == mySearcher ? SearchEngine::StaticOptions : 0, REG_ICASE | Config.regex_type);
1772 if (findme.empty())
1773 continue;
1775 success ? ShowMessage("Searching finished!") : ShowMessage("Unable to find \"%s\"", findme.c_str());
1777 if (Keypressed(input, Key.FindForward))
1778 mList->NextFound(Config.wrapped_search);
1779 else
1780 mList->PrevFound(Config.wrapped_search);
1782 if (myScreen == myPlaylist)
1783 myPlaylist->EnableHighlighting();
1785 else if (myScreen == myHelp || myScreen == myLyrics || myScreen == myInfo)
1787 LockStatusbar();
1788 Statusbar() << "Find: ";
1789 std::string findme = wFooter->GetString();
1790 UnlockStatusbar();
1792 ShowMessage("Searching...");
1793 Screen<Scrollpad> *s = static_cast<Screen<Scrollpad> *>(myScreen);
1794 s->Main()->RemoveFormatting();
1795 ShowMessage("%s", findme.empty() || s->Main()->SetFormatting(fmtReverse, TO_WSTRING(findme), fmtReverseEnd) ? "Done!" : "No matching patterns found");
1796 s->Main()->Flush();
1799 else if (Keypressed(input, Key.NextFoundPosition) || Keypressed(input, Key.PrevFoundPosition))
1801 List *mList = myScreen->GetList();
1803 if (!mList)
1804 continue;
1806 if (Keypressed(input, Key.NextFoundPosition))
1807 mList->NextFound(Config.wrapped_search);
1808 else
1809 mList->PrevFound(Config.wrapped_search);
1811 else if (Keypressed(input, Key.ToggleFindMode))
1813 Config.wrapped_search = !Config.wrapped_search;
1814 ShowMessage("Search mode: %s", Config.wrapped_search ? "Wrapped" : "Normal");
1816 else if (Keypressed(input, Key.ToggleReplayGainMode) && Mpd.Version() >= 16)
1818 LockStatusbar();
1819 Statusbar() << "Replay gain mode ? [" << fmtBold << 'o' << fmtBoldEnd << "ff/" << fmtBold << 't' << fmtBoldEnd << "rack/" << fmtBold << 'a' << fmtBoldEnd << "lbum]";
1820 wFooter->Refresh();
1821 input = 0;
1824 TraceMpdStatus();
1825 wFooter->ReadKey(input);
1827 while (input != 'o' && input != 't' && input != 'a');
1828 UnlockStatusbar();
1829 Mpd.SetReplayGainMode(input == 't' ? rgmTrack : (input == 'a' ? rgmAlbum : rgmOff));
1830 ShowMessage("Replay gain mode: %s", Mpd.GetReplayGainMode().c_str());
1832 else if (Keypressed(input, Key.ToggleSpaceMode))
1834 Config.space_selects = !Config.space_selects;
1835 ShowMessage("Space mode: %s item", Config.space_selects ? "Select/deselect" : "Add");
1837 else if (Keypressed(input, Key.ToggleAddMode))
1839 Config.ncmpc_like_songs_adding = !Config.ncmpc_like_songs_adding;
1840 ShowMessage("Add mode: %s", Config.ncmpc_like_songs_adding ? "Add item to playlist, remove if already added" : "Always add item to playlist");
1842 else if (Keypressed(input, Key.ToggleMouse))
1844 Config.mouse_support = !Config.mouse_support;
1845 mousemask(Config.mouse_support ? ALL_MOUSE_EVENTS : 0, 0);
1846 ShowMessage("Mouse support %s", Config.mouse_support ? "enabled" : "disabled");
1848 else if (Keypressed(input, Key.SwitchTagTypeList))
1850 if (myScreen == myPlaylist)
1852 LockStatusbar();
1853 Statusbar() << "Number of random songs: ";
1854 size_t number = StrToLong(wFooter->GetString());
1855 UnlockStatusbar();
1856 if (number && Mpd.AddRandomSongs(number))
1857 ShowMessage("%zu random song%s added to playlist!", number, number == 1 ? "" : "s");
1859 else if (myScreen == myBrowser && !myBrowser->isLocal())
1861 Config.browser_sort_by_mtime = !Config.browser_sort_by_mtime;
1862 myBrowser->Main()->Sort<CaseInsensitiveSorting>(myBrowser->CurrentDir() != "/");
1863 ShowMessage("Sort songs by: %s", Config.browser_sort_by_mtime ? "Modification time" : "Name");
1865 else if (myScreen->ActiveWindow() == myLibrary->Artists
1866 || (myLibrary->Columns() == 2 && myScreen->ActiveWindow() == myLibrary->Albums))
1868 LockStatusbar();
1869 Statusbar() << "Tag type ? [" << fmtBold << 'a' << fmtBoldEnd << "rtist/" << fmtBold << 'y' << fmtBoldEnd << "ear/" << fmtBold << 'g' << fmtBoldEnd << "enre/" << fmtBold << 'c' << fmtBoldEnd << "omposer/" << fmtBold << 'p' << fmtBoldEnd << "erformer] ";
1870 wFooter->Refresh();
1871 input = 0;
1874 TraceMpdStatus();
1875 wFooter->ReadKey(input);
1877 while (input != 'a' && input != 'y' && input != 'g' && input != 'c' && input != 'p');
1878 UnlockStatusbar();
1879 mpd_tag_type new_tagitem = IntoTagItem(input);
1880 if (new_tagitem != Config.media_lib_primary_tag)
1882 Config.media_lib_primary_tag = new_tagitem;
1883 std::string item_type = IntoStr(Config.media_lib_primary_tag);
1884 myLibrary->Artists->SetTitle(item_type + "s");
1885 myLibrary->Artists->Reset();
1886 ToLower(item_type);
1887 if (myLibrary->Columns() == 2)
1889 myLibrary->Songs->Clear(0);
1890 myLibrary->Albums->Reset();
1891 myLibrary->Albums->Clear();
1892 myLibrary->Albums->SetTitle("Albums (sorted by " + item_type + ")");
1893 myLibrary->Albums->Display();
1895 else
1897 myLibrary->Artists->Clear(0);
1898 myLibrary->Artists->Display();
1900 ShowMessage("Switched to list of %s tag", item_type.c_str());
1903 else if (myScreen == myLyrics)
1905 myLyrics->FetchAgain();
1908 else if (Keypressed(input, Key.SongInfo))
1910 myInfo->GetSong();
1912 # ifdef HAVE_CURL_CURL_H
1913 else if (Keypressed(input, Key.ArtistInfo))
1915 myInfo->GetArtist();
1917 # endif // HAVE_CURL_CURL_H
1918 else if (Keypressed(input, Key.Lyrics))
1920 myLyrics->SwitchTo();
1922 else if (Keypressed(input, Key.Quit))
1924 main_exit = 1;
1926 # ifdef HAVE_TAGLIB_H
1927 else if (myScreen == myTinyTagEditor)
1929 continue;
1931 # endif // HAVE_TAGLIB_H
1932 else if (Keypressed(input, Key.Help))
1934 myHelp->SwitchTo();
1936 else if (Keypressed(input, Key.ScreenSwitcher))
1938 if (myScreen == myPlaylist)
1939 myBrowser->SwitchTo();
1940 else
1941 myPlaylist->SwitchTo();
1943 else if (Keypressed(input, Key.Playlist))
1945 myPlaylist->SwitchTo();
1947 else if (Keypressed(input, Key.Browser))
1949 myBrowser->SwitchTo();
1951 else if (Keypressed(input, Key.SearchEngine))
1953 mySearcher->SwitchTo();
1955 else if (Keypressed(input, Key.MediaLibrary))
1957 myLibrary->SwitchTo();
1959 else if (Keypressed(input, Key.PlaylistEditor))
1961 myPlaylistEditor->SwitchTo();
1963 # ifdef HAVE_TAGLIB_H
1964 else if (Keypressed(input, Key.TagEditor))
1966 CHECK_MPD_MUSIC_DIR;
1967 myTagEditor->SwitchTo();
1969 # endif // HAVE_TAGLIB_H
1970 # ifdef ENABLE_OUTPUTS
1971 else if (Keypressed(input, Key.Outputs))
1973 myOutputs->SwitchTo();
1975 # endif // ENABLE_OUTPUTS
1976 # ifdef ENABLE_VISUALIZER
1977 else if (Keypressed(input, Key.Visualizer))
1979 myVisualizer->SwitchTo();
1981 # endif // ENABLE_VISUALIZER
1982 # ifdef ENABLE_CLOCK
1983 else if (Keypressed(input, Key.Clock))
1985 myClock->SwitchTo();
1987 # endif // ENABLE_CLOCK
1988 else if (Keypressed(input, Key.ServerInfo))
1990 myServerInfo->SwitchTo();
1992 // key mapping end
1994 return 0;