1 /***************************************************************************
2 * Copyright (C) 2008-2009 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
41 #include "media_library.h"
43 #include "server_info.h"
46 #include "playlist_editor.h"
47 #include "search_engine.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()) \
60 ShowMessage("%s", MPD::Message::FunctionDisabledFilteringEnabled); \
64 #define CHECK_MPD_MUSIC_DIR \
65 if (Config.mpd_music_dir.empty()) \
67 ShowMessage("configuration variable mpd_music_dir is not set!"); \
71 using namespace Global
;
74 BasicScreen
*Global::myScreen
;
75 BasicScreen
*Global::myOldScreen
;
76 BasicScreen
*Global::myPrevScreen
;
78 Window
*Global::wHeader
;
79 Window
*Global::wFooter
;
81 size_t Global::MainStartY
;
82 size_t Global::MainHeight
;
84 bool Global::BlockItemListUpdate
= 0;
86 bool Global::MessagesAllowed
= 0;
87 bool Global::SeekingInProgress
= 0;
88 bool Global::RedrawHeader
= 1;
92 std::ofstream errorlog
;
93 std::streambuf
*cerr_buffer
;
95 bool design_changed
= 0;
96 bool order_resize
= 0;
97 size_t header_height
, footer_start_y
, footer_height
;
103 # if defined(USE_PDCURSES)
106 // update internal screen dimensions
111 // get rid of KEY_RESIZE as it sometimes
112 // corrupts our new cool ReadKey() function
113 // because KEY_RESIZE doesn't come from stdin
114 // and thus select cannot detect it
122 MainHeight
= LINES
-(Config
.new_design
? 7 : 4);
124 if (COLS
< 20 || MainHeight
< 3)
127 std::cout
<< "Screen is too small!\n";
131 if (!Config
.header_visibility
)
133 if (!Config
.statusbar_visibility
)
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
160 myClock
->hasToBeResized
= 1;
161 # endif // ENABLE_CLOCK
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);
174 StatusChanges changes
;
175 if (!Mpd
.isPlaying() || design_changed
)
177 changes
.PlayerState
= 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);
189 NcmpcppStatusChanged(&Mpd
, StatusChanges(), 0);
191 ShowMessage("User interface: %s", Config
.new_design
? "Alternative" : "Classic");
197 void sighandler(int signal
)
199 if (signal
== SIGPIPE
)
201 ShowMessage("Broken pipe signal caught!");
203 else if (signal
== SIGWINCH
)
212 // restore old cerr buffer
213 std::cerr
.rdbuf(cerr_buffer
);
216 # ifndef USE_PDCURSES // destroying screen somehow crashes pdcurses
218 # endif // USE_PDCURSES
223 int main(int argc
, char *argv
[])
225 setlocale(LC_ALL
, "");
228 DefaultConfiguration(Config
);
230 ReadConfiguration(Config
);
233 if (getenv("MPD_HOST"))
234 Mpd
.SetHostname(getenv("MPD_HOST"));
235 if (getenv("MPD_PORT"))
236 Mpd
.SetPort(atoi(getenv("MPD_PORT")));
238 if (Config
.mpd_host
!= "localhost")
239 Mpd
.SetHostname(Config
.mpd_host
);
240 if (Config
.mpd_port
!= 6600)
241 Mpd
.SetPort(Config
.mpd_port
);
243 Mpd
.SetTimeout(Config
.mpd_connection_timeout
);
244 Mpd
.SetIdleEnabled(Config
.enable_idle_notifications
);
247 ParseArgv(argc
, argv
);
252 // always execute these commands, even if ncmpcpp use exit function
255 // redirect std::cerr output to ~/.ncmpcpp/error.log file
256 errorlog
.open((config_dir
+ "error.log").c_str(), std::ios::app
);
257 cerr_buffer
= std::cerr
.rdbuf();
258 std::cerr
.rdbuf(errorlog
.rdbuf());
260 InitScreen("ncmpc++ ver. "VERSION
, Config
.colors_enabled
);
262 bool real_statusbar_visibility
= Config
.statusbar_visibility
;
264 if (Config
.new_design
)
265 Config
.statusbar_visibility
= 0;
267 SetWindowsDimensions(header_height
, footer_start_y
, footer_height
);
269 if (Config
.header_visibility
|| Config
.new_design
)
271 wHeader
= new Window(0, 0, COLS
, header_height
, "", Config
.header_color
, brNone
);
275 wFooter
= new Window(0, footer_start_y
, COLS
, footer_height
, "", Config
.statusbar_color
, brNone
);
276 wFooter
->SetTimeout(ncmpcpp_window_timeout
);
277 wFooter
->SetGetStringHelper(StatusbarGetStringHelper
);
278 wFooter
->AddFDCallback(Mpd
.GetFD(), StatusbarMPDCallback
);
279 wFooter
->CreateHistory();
281 // initialize screens to browser as default previous screen
282 myScreen
= myBrowser
;
283 myPrevScreen
= myBrowser
;
284 myOldScreen
= myBrowser
;
287 myPlaylist
->SwitchTo();
288 myPlaylist
->UpdateTimer();
290 Mpd
.SetStatusUpdater(NcmpcppStatusChanged
, 0);
291 Mpd
.SetErrorHandler(NcmpcppErrorCallback
, 0);
297 bool title_allowed
= !Config
.display_screens_numbers_on_start
;
299 std::string screen_title
;
301 timeval past
= { 0, 0 };
302 // local variables end
305 signal(SIGPIPE
, sighandler
);
306 signal(SIGWINCH
, sighandler
);
311 if (Config
.mouse_support
)
312 mousemask(ALL_MOUSE_EVENTS
, 0);
314 if (Config
.jump_to_now_playing_song_at_start
)
317 if (myPlaylist
->isPlaying())
318 myPlaylist
->Items
->Highlight(myPlaylist
->NowPlaying
);
323 if (!Mpd
.Connected())
325 if (!wFooter
->FDCallbacksListEmpty())
326 wFooter
->ClearFDCallbacksList();
327 ShowMessage("Attempting to reconnect...");
330 ShowMessage("Connected to %s!", Mpd
.GetHostname().c_str());
331 wFooter
->AddFDCallback(Mpd
.GetFD(), StatusbarMPDCallback
);
333 UpdateStatusImmediately
= 1;
334 # ifdef ENABLE_VISUALIZER
335 myVisualizer
->ResetFD();
336 if (myScreen
== myVisualizer
)
337 myVisualizer
->SetFD();
338 myVisualizer
->FindOutputID();
339 # endif // ENABLE_VISUALIZER
351 if (((Timer
.tv_sec
== past
.tv_sec
&& Timer
.tv_usec
>= past
.tv_usec
+500000) || Timer
.tv_sec
> past
.tv_sec
)
352 && (myScreen
== myPlaylist
|| myScreen
== myBrowser
|| myScreen
== myLyrics
)
356 gettimeofday(&past
, 0);
358 if (Config
.header_visibility
&& RedrawHeader
)
362 if (Config
.new_design
)
364 std::basic_string
<my_char_t
> title
= myScreen
->Title();
365 *wHeader
<< XY(0, 3) << wclrtoeol
;
366 *wHeader
<< fmtBold
<< Config
.alternative_ui_separator_color
;
367 mvwhline(wHeader
->Raw(), 2, 0, 0, COLS
);
368 mvwhline(wHeader
->Raw(), 4, 0, 0, COLS
);
369 *wHeader
<< XY((COLS
-Window::Length(title
))/2, 3);
370 *wHeader
<< Config
.header_color
<< title
<< clEnd
;
371 *wHeader
<< clEnd
<< fmtBoldEnd
;
374 *wHeader
<< XY(0, 0) << wclrtoeol
<< fmtBold
<< myScreen
->Title() << fmtBoldEnd
;
378 *wHeader
<< XY(0, Config
.new_design
? 3 : 0)
379 << fmtBold
<< char(Key
.Help
[0]) << fmtBoldEnd
<< ":Help "
380 << fmtBold
<< char(Key
.Playlist
[0]) << fmtBoldEnd
<< ":Playlist "
381 << fmtBold
<< char(Key
.Browser
[0]) << fmtBoldEnd
<< ":Browse "
382 << fmtBold
<< char(Key
.SearchEngine
[0]) << fmtBoldEnd
<< ":Search "
383 << fmtBold
<< char(Key
.MediaLibrary
[0]) << fmtBoldEnd
<< ":Library "
384 << fmtBold
<< char(Key
.PlaylistEditor
[0]) << fmtBoldEnd
<< ":Playlist editor";
385 # ifdef HAVE_TAGLIB_H
386 *wHeader
<< " " << fmtBold
<< char(Key
.TagEditor
[0]) << fmtBoldEnd
<< ":Tag editor";
387 # endif // HAVE_TAGLIB_H
388 # ifdef ENABLE_VISUALIZER
389 *wHeader
<< " " << fmtBold
<< char(Key
.Visualizer
[0]) << fmtBoldEnd
<< ":Music visualizer";
390 # endif // ENABLE_VISUALIZER
392 *wHeader
<< " " << fmtBold
<< char(Key
.Clock
[0]) << fmtBoldEnd
<< ":Clock";
393 # endif // ENABLE_CLOCK
394 if (Config
.new_design
)
396 *wHeader
<< fmtBold
<< Config
.alternative_ui_separator_color
;
397 mvwhline(wHeader
->Raw(), 2, 0, 0, COLS
);
398 mvwhline(wHeader
->Raw(), 4, 0, 0, COLS
);
399 *wHeader
<< clEnd
<< fmtBoldEnd
;
402 if (!Config
.new_design
)
404 *wHeader
<< Config
.volume_color
;
405 *wHeader
<< XY(wHeader
->GetWidth()-VolumeState
.length(), 0) << VolumeState
;
414 myScreen
->RefreshWindow();
415 wFooter
->ReadKey(input
);
424 if (myScreen
== myPlaylist
)
425 myPlaylist
->EnableHighlighting();
427 // key mapping beginning
429 if (Keypressed(input
, Key
.Up
))
431 myScreen
->Scroll(wUp
, Key
.Up
);
433 else if (Keypressed(input
, Key
.Down
))
435 myScreen
->Scroll(wDown
, Key
.Down
);
437 else if (Keypressed(input
, Key
.PageUp
))
439 myScreen
->Scroll(wPageUp
, Key
.PageUp
);
441 else if (Keypressed(input
, Key
.PageDown
))
443 myScreen
->Scroll(wPageDown
, Key
.PageDown
);
445 else if (Keypressed(input
, Key
.Home
))
447 myScreen
->Scroll(wHome
);
449 else if (Keypressed(input
, Key
.End
))
451 myScreen
->Scroll(wEnd
);
453 else if (Config
.mouse_support
&& input
== KEY_MOUSE
)
455 getmouse(&mouse_event
);
456 if (mouse_event
.bstate
& BUTTON1_PRESSED
457 && mouse_event
.y
== LINES
-(Config
.statusbar_visibility
? 2 : 1)
460 if (!myPlaylist
->isPlaying())
462 Mpd
.Seek(Mpd
.GetTotalTime()*mouse_event
.x
/double(COLS
));
463 UpdateStatusImmediately
= 1;
465 else if (mouse_event
.bstate
& BUTTON1_PRESSED
466 && (Config
.statusbar_visibility
|| Config
.new_design
)
468 && mouse_event
.y
== (Config
.new_design
? 1 : LINES
-1) && mouse_event
.x
< 9
472 UpdateStatusImmediately
= 1;
474 else if ((mouse_event
.bstate
& BUTTON2_PRESSED
|| mouse_event
.bstate
& BUTTON4_PRESSED
)
475 && Config
.header_visibility
476 && mouse_event
.y
== 0 && size_t(mouse_event
.x
) > COLS
-VolumeState
.length()
479 if (mouse_event
.bstate
& BUTTON2_PRESSED
)
480 Mpd
.SetVolume(Mpd
.GetVolume()-2);
482 Mpd
.SetVolume(Mpd
.GetVolume()+2);
484 else if (mouse_event
.bstate
& (BUTTON1_PRESSED
| BUTTON2_PRESSED
| BUTTON3_PRESSED
| BUTTON4_PRESSED
))
485 myScreen
->MouseButtonPressed(mouse_event
);
487 if (Keypressed(input
, Key
.ToggleInterface
))
489 Config
.new_design
= !Config
.new_design
;
490 Config
.statusbar_visibility
= Config
.new_design
? 0 : real_statusbar_visibility
;
491 SetWindowsDimensions(header_height
, footer_start_y
, footer_height
);
497 else if (Keypressed(input
, Key
.GoToParentDir
))
499 if (myScreen
== myBrowser
&& myBrowser
->CurrentDir() != "/")
501 myBrowser
->Main()->Reset();
502 myBrowser
->EnterPressed();
504 # ifdef HAVE_TAGLIB_H
505 else if (myScreen
->ActiveWindow() == myTagEditor
->Dirs
&& myTagEditor
->CurrentDir() != "/")
507 myTagEditor
->Dirs
->Reset();
508 myTagEditor
->EnterPressed();
510 # endif // HAVE_TAGLIB_H
512 else if (Keypressed(input
, Key
.Enter
))
514 myScreen
->EnterPressed();
516 else if (Keypressed(input
, Key
.Space
))
518 myScreen
->SpacePressed();
520 else if (Keypressed(input
, Key
.VolumeUp
))
522 if (myScreen
== myLibrary
&& input
== Key
.VolumeUp
[0])
524 myLibrary
->NextColumn();
526 else if (myScreen
== myPlaylistEditor
&& input
== Key
.VolumeUp
[0])
528 myPlaylistEditor
->NextColumn();
530 # ifdef HAVE_TAGLIB_H
531 else if (myScreen
== myTagEditor
&& input
== Key
.VolumeUp
[0])
533 myTagEditor
->NextColumn();
535 # endif // HAVE_TAGLIB_H
537 Mpd
.SetVolume(Mpd
.GetVolume()+1);
539 else if (Keypressed(input
, Key
.VolumeDown
))
541 if (myScreen
== myLibrary
&& input
== Key
.VolumeDown
[0])
543 myLibrary
->PrevColumn();
545 else if (myScreen
== myPlaylistEditor
&& input
== Key
.VolumeDown
[0])
547 myPlaylistEditor
->PrevColumn();
549 # ifdef HAVE_TAGLIB_H
550 else if (myScreen
== myTagEditor
&& input
== Key
.VolumeDown
[0])
552 myTagEditor
->PrevColumn();
554 # endif // HAVE_TAGLIB_H
556 Mpd
.SetVolume(Mpd
.GetVolume()-1);
558 else if (Keypressed(input
, Key
.Delete
))
560 if (!myPlaylist
->Items
->Empty() && myScreen
== myPlaylist
)
562 if (myPlaylist
->Items
->hasSelected())
564 std::vector
<size_t> list
;
565 myPlaylist
->Items
->GetSelected(list
);
566 Mpd
.StartCommandsList();
567 for (std::vector
<size_t>::reverse_iterator it
= list
.rbegin(); it
!= list
.rend(); ++it
)
568 Mpd
.DeleteID((*myPlaylist
->Items
)[*it
].GetID());
569 if (Mpd
.CommitCommandsList())
571 for (size_t i
= 0; i
< myPlaylist
->Items
->Size(); ++i
)
572 myPlaylist
->Items
->Select(i
, 0);
573 myPlaylist
->FixPositions(list
.front());
574 ShowMessage("Selected items deleted!");
579 Playlist::BlockNowPlayingUpdate
= 1;
580 wFooter
->SetTimeout(50);
582 while (!myPlaylist
->Items
->Empty() && Keypressed(input
, Key
.Delete
))
584 size_t id
= myPlaylist
->Items
->Choice();
586 Playlist::BlockUpdate
= 1;
587 myPlaylist
->UpdateTimer();
588 // needed for keeping proper position of now playing song.
589 if (myPlaylist
->NowPlaying
> int(myPlaylist
->CurrentSong()->GetPosition())-del_counter
)
590 --myPlaylist
->NowPlaying
;
591 if (Mpd
.DeleteID(myPlaylist
->CurrentSong()->GetID()))
593 myPlaylist
->Items
->DeleteOption(id
);
594 myPlaylist
->Items
->Refresh();
595 wFooter
->ReadKey(input
);
601 myPlaylist
->FixPositions(myPlaylist
->Items
->Choice());
602 wFooter
->SetTimeout(ncmpcpp_window_timeout
);
603 Playlist::BlockNowPlayingUpdate
= 0;
607 (myScreen
== myBrowser
&& !myBrowser
->Main()->Empty() && myBrowser
->Main()->Current().type
== itPlaylist
)
608 || (myScreen
->ActiveWindow() == myPlaylistEditor
->Playlists
)
611 std::string name
= myScreen
== myBrowser
? myBrowser
->Main()->Current().name
: myPlaylistEditor
->Playlists
->Current();
613 Statusbar() << "Delete playlist \"" << Shorten(TO_WSTRING(name
), COLS
-28) << "\" ? [" << fmtBold
<< 'y' << fmtBoldEnd
<< '/' << fmtBold
<< 'n' << fmtBoldEnd
<< "]";
619 wFooter
->ReadKey(answer
);
621 while (answer
!= 'y' && answer
!= 'n');
625 if (Mpd
.DeletePlaylist(locale_to_utf_cpy(name
)))
627 static const char msg
[] = "Playlist \"%s\" deleted!";
628 ShowMessage(msg
, Shorten(TO_WSTRING(name
), COLS
-static_strlen(msg
)).c_str());
629 if (myBrowser
->Main() && !myBrowser
->isLocal() && myBrowser
->CurrentDir() == "/")
630 myBrowser
->GetDirectory("/");
634 ShowMessage("Aborted!");
635 if (myPlaylistEditor
->Main()) // check if initialized
636 myPlaylistEditor
->Playlists
->Clear(); // make playlists list update itself
639 else if (myScreen
== myBrowser
&& !myBrowser
->Main()->Empty() && myBrowser
->Main()->Current().type
!= itPlaylist
)
641 if (!myBrowser
->isLocal())
644 MPD::Item
&item
= myBrowser
->Main()->Current();
646 if (item
.type
== itSong
&& !Config
.allow_physical_files_deletion
)
648 ShowMessage("Deleting files is disabled by default, see man page for more details");
651 if (item
.type
== itDirectory
&& !Config
.allow_physical_directories_deletion
)
653 ShowMessage("Deleting directories is disabled by default, see man page for more details");
656 if (item
.type
== itDirectory
&& item
.song
) // parent dir
659 std::string name
= item
.type
== itSong
? item
.song
->GetName() : item
.name
;
661 Statusbar() << "Delete " << (item
.type
== itSong
? "file" : "directory") << " \"" << Shorten(TO_WSTRING(name
), COLS
-30) << "\" ? [" << fmtBold
<< 'y' << fmtBoldEnd
<< '/' << fmtBold
<< 'n' << fmtBoldEnd
<< "] ";
667 wFooter
->ReadKey(answer
);
669 while (answer
!= 'y' && answer
!= 'n');
674 if (!myBrowser
->isLocal())
675 path
= Config
.mpd_music_dir
;
676 path
+= item
.type
== itSong
? item
.song
->GetFile() : item
.name
;
678 if (item
.type
== itDirectory
)
679 myBrowser
->ClearDirectory(path
);
681 if (remove(path
.c_str()) == 0)
683 static const char msg
[] = "\"%s\" deleted!";
684 ShowMessage(msg
, Shorten(TO_WSTRING(name
), COLS
-static_strlen(msg
)).c_str());
685 if (!myBrowser
->isLocal())
686 Mpd
.UpdateDirectory(myBrowser
->CurrentDir());
688 myBrowser
->GetDirectory(myBrowser
->CurrentDir());
692 static const char msg
[] = "Couldn't remove \"%s\": %s";
693 ShowMessage(msg
, Shorten(TO_WSTRING(name
), COLS
-static_strlen(msg
)-25).c_str(), strerror(errno
));
697 ShowMessage("Aborted!");
701 else if (myScreen
->ActiveWindow() == myPlaylistEditor
->Content
&& !myPlaylistEditor
->Content
->Empty())
703 if (myPlaylistEditor
->Content
->hasSelected())
705 std::vector
<size_t> list
;
706 myPlaylistEditor
->Content
->GetSelected(list
);
707 std::string playlist
= locale_to_utf_cpy(myPlaylistEditor
->Playlists
->Current());
708 ShowMessage("Deleting selected items...");
709 Mpd
.StartCommandsList();
710 for (std::vector
<size_t>::reverse_iterator it
= list
.rbegin(); it
!= list
.rend(); ++it
)
712 Mpd
.Delete(playlist
, *it
);
713 myPlaylistEditor
->Content
->DeleteOption(*it
);
715 Mpd
.CommitCommandsList();
716 ShowMessage("Selected items deleted from playlist \"%s\"!", myPlaylistEditor
->Playlists
->Current().c_str());
720 wFooter
->SetTimeout(50);
721 locale_to_utf(myPlaylistEditor
->Playlists
->Current());
722 while (!myPlaylistEditor
->Content
->Empty() && Keypressed(input
, Key
.Delete
))
725 myPlaylist
->UpdateTimer();
726 Mpd
.Delete(myPlaylistEditor
->Playlists
->Current(), myPlaylistEditor
->Content
->Choice());
727 myPlaylistEditor
->Content
->DeleteOption(myPlaylistEditor
->Content
->Choice());
728 myPlaylistEditor
->Content
->Refresh();
729 wFooter
->ReadKey(input
);
731 utf_to_locale(myPlaylistEditor
->Playlists
->Current());
732 wFooter
->SetTimeout(ncmpcpp_window_timeout
);
736 else if (Keypressed(input
, Key
.Prev
))
739 UpdateStatusImmediately
= 1;
741 else if (Keypressed(input
, Key
.Next
))
744 UpdateStatusImmediately
= 1;
746 else if (Keypressed(input
, Key
.Pause
))
749 UpdateStatusImmediately
= 1;
751 else if (Keypressed(input
, Key
.SavePlaylist
))
754 Statusbar() << "Save playlist as: ";
755 std::string playlist_name
= wFooter
->GetString();
756 std::string real_playlist_name
= locale_to_utf_cpy(playlist_name
);
758 if (playlist_name
.find("/") != std::string::npos
)
760 ShowMessage("Playlist name cannot contain slashes!");
763 if (!playlist_name
.empty())
765 if (myPlaylist
->Items
->isFiltered())
767 Mpd
.StartCommandsList();
768 for (size_t i
= 0; i
< myPlaylist
->Items
->Size(); ++i
)
769 Mpd
.AddToPlaylist(real_playlist_name
, (*myPlaylist
->Items
)[i
]);
770 Mpd
.CommitCommandsList();
771 if (Mpd
.GetErrorMessage().empty())
772 ShowMessage("Filtered items added to playlist \"%s\"", playlist_name
.c_str());
774 else if (Mpd
.SavePlaylist(real_playlist_name
))
776 ShowMessage("Playlist saved as: %s", playlist_name
.c_str());
777 if (myPlaylistEditor
->Main()) // check if initialized
778 myPlaylistEditor
->Playlists
->Clear(); // make playlist's list update itself
783 Statusbar() << "Playlist already exists, overwrite: " << playlist_name
<< " ? [" << fmtBold
<< 'y' << fmtBoldEnd
<< '/' << fmtBold
<< 'n' << fmtBoldEnd
<< "] ";
786 while (answer
!= 'y' && answer
!= 'n')
789 wFooter
->ReadKey(answer
);
795 Mpd
.DeletePlaylist(real_playlist_name
);
796 if (Mpd
.SavePlaylist(real_playlist_name
))
797 ShowMessage("Playlist overwritten!");
800 ShowMessage("Aborted!");
801 if (myPlaylistEditor
->Main()) // check if initialized
802 myPlaylistEditor
->Playlists
->Clear(); // make playlist's list update itself
803 if (myScreen
== myPlaylist
)
804 myPlaylist
->EnableHighlighting();
807 if (myBrowser
->Main()
808 && !myBrowser
->isLocal()
809 && myBrowser
->CurrentDir() == "/"
810 && !myBrowser
->Main()->Empty())
811 myBrowser
->GetDirectory(myBrowser
->CurrentDir());
813 else if (Keypressed(input
, Key
.Stop
))
816 UpdateStatusImmediately
= 1;
818 else if (Keypressed(input
, Key
.MvSongUp
))
820 if (myScreen
== myPlaylist
&& myPlaylist
->SortingInProgress())
821 myPlaylist
->AdjustSortOrder(input
);
822 else if (myScreen
== myPlaylist
&& !myPlaylist
->Items
->Empty())
824 CHECK_PLAYLIST_FOR_FILTERING
;
825 wFooter
->SetTimeout(50);
826 if (myPlaylist
->Items
->hasSelected())
828 std::vector
<size_t> list
;
829 myPlaylist
->Items
->GetSelected(list
);
830 std::vector
<size_t> origs(list
);
832 // NOTICE: since ncmpcpp only pretends to move the songs until the key is
833 // released, mpd doesn't know about the change while the songs are moved
834 // so wee need to block playlist update for this time and also if one of
835 // the songs being moved is currently playing, now playing update to prevent
836 // mpd from 'updating' and thus showing wrong position
838 bool modify_now_playing
= 0;
839 for (std::vector
<size_t>::iterator it
= list
.begin(); it
!= list
.end(); ++it
)
841 if (*it
== size_t(myPlaylist
->NowPlaying
) && list
.front() > 0)
843 modify_now_playing
= 1;
844 Playlist::BlockNowPlayingUpdate
= 1;
849 while (Keypressed(input
, Key
.MvSongUp
) && list
.front() > 0)
852 Playlist::BlockUpdate
= 1;
853 myPlaylist
->UpdateTimer();
854 if (modify_now_playing
)
855 --myPlaylist
->NowPlaying
;
856 for (std::vector
<size_t>::iterator it
= list
.begin(); it
!= list
.end(); ++it
)
859 myPlaylist
->Items
->at((*it
)+1).SetPosition(*it
);
860 myPlaylist
->Items
->at(*it
).SetPosition((*it
)+1);
861 myPlaylist
->Items
->Swap(*it
, (*it
)+1);
863 myPlaylist
->Items
->Highlight(list
[(list
.size()-1)/2]);
864 myPlaylist
->Items
->Refresh();
865 wFooter
->ReadKey(input
);
867 Playlist::BlockNowPlayingUpdate
= 0;
868 Mpd
.StartCommandsList();
869 for (size_t i
= 0; i
< list
.size(); ++i
)
870 Mpd
.Move(origs
[i
], list
[i
]);
871 Mpd
.CommitCommandsList();
876 from
= to
= myPlaylist
->Items
->Choice();
877 bool modify_now_playing
= from
== size_t(myPlaylist
->NowPlaying
);
878 if (modify_now_playing
)
879 Playlist::BlockNowPlayingUpdate
= 1;
880 while (Keypressed(input
, Key
.MvSongUp
) && to
> 0)
883 Playlist::BlockUpdate
= 1;
884 myPlaylist
->UpdateTimer();
885 if (modify_now_playing
)
886 --myPlaylist
->NowPlaying
;
888 myPlaylist
->Items
->at(from
).SetPosition(to
);
889 myPlaylist
->Items
->at(to
).SetPosition(from
);
890 myPlaylist
->Items
->Swap(to
, to
+1);
891 myPlaylist
->Items
->Scroll(wUp
);
892 myPlaylist
->Items
->Refresh();
893 wFooter
->ReadKey(input
);
896 Playlist::BlockNowPlayingUpdate
= 0;
897 UpdateStatusImmediately
= 1;
899 wFooter
->SetTimeout(ncmpcpp_window_timeout
);
901 else if (myScreen
->ActiveWindow() == myPlaylistEditor
->Content
&& !myPlaylistEditor
->Content
->Empty())
903 wFooter
->SetTimeout(50);
904 if (myPlaylistEditor
->Content
->hasSelected())
906 std::vector
<size_t> list
;
907 myPlaylistEditor
->Content
->GetSelected(list
);
909 std::vector
<size_t> origs(list
);
911 while (Keypressed(input
, Key
.MvSongUp
) && list
.front() > 0)
914 myPlaylist
->UpdateTimer();
915 for (std::vector
<size_t>::iterator it
= list
.begin(); it
!= list
.end(); ++it
)
918 myPlaylistEditor
->Content
->Swap(*it
, (*it
)+1);
920 myPlaylistEditor
->Content
->Highlight(list
[(list
.size()-1)/2]);
921 myPlaylistEditor
->Content
->Refresh();
922 wFooter
->ReadKey(input
);
924 Mpd
.StartCommandsList();
925 for (size_t i
= 0; i
< list
.size(); ++i
)
926 if (origs
[i
] != list
[i
])
927 Mpd
.Move(myPlaylistEditor
->Playlists
->Current(), origs
[i
], list
[i
]);
928 Mpd
.CommitCommandsList();
933 from
= to
= myPlaylistEditor
->Content
->Choice();
934 while (Keypressed(input
, Key
.MvSongUp
) && to
> 0)
937 myPlaylist
->UpdateTimer();
939 myPlaylistEditor
->Content
->Swap(to
, to
+1);
940 myPlaylistEditor
->Content
->Scroll(wUp
);
941 myPlaylistEditor
->Content
->Refresh();
942 wFooter
->ReadKey(input
);
945 Mpd
.Move(myPlaylistEditor
->Playlists
->Current(), from
, to
);
947 wFooter
->SetTimeout(ncmpcpp_window_timeout
);
950 else if (Keypressed(input
, Key
.MvSongDown
))
952 if (myScreen
== myPlaylist
&& myPlaylist
->SortingInProgress())
953 myPlaylist
->AdjustSortOrder(input
);
954 else if (myScreen
== myPlaylist
&& !myPlaylist
->Items
->Empty())
956 CHECK_PLAYLIST_FOR_FILTERING
;
957 wFooter
->SetTimeout(50);
958 if (myPlaylist
->Items
->hasSelected())
960 std::vector
<size_t> list
;
961 myPlaylist
->Items
->GetSelected(list
);
962 std::vector
<size_t> origs(list
);
964 bool modify_now_playing
= 0;
965 for (std::vector
<size_t>::iterator it
= list
.begin(); it
!= list
.end(); ++it
)
967 if (*it
== size_t(myPlaylist
->NowPlaying
) && list
.back() < myPlaylist
->Items
->Size()-1)
969 modify_now_playing
= 1;
970 Playlist::BlockNowPlayingUpdate
= 1;
975 while (Keypressed(input
, Key
.MvSongDown
) && list
.back() < myPlaylist
->Items
->Size()-1)
978 Playlist::BlockUpdate
= 1;
979 myPlaylist
->UpdateTimer();
980 if (modify_now_playing
)
981 ++myPlaylist
->NowPlaying
;
982 for (std::vector
<size_t>::reverse_iterator it
= list
.rbegin(); it
!= list
.rend(); ++it
)
985 myPlaylist
->Items
->at((*it
)-1).SetPosition(*it
);
986 myPlaylist
->Items
->at(*it
).SetPosition((*it
)-1);
987 myPlaylist
->Items
->Swap(*it
, (*it
)-1);
989 myPlaylist
->Items
->Highlight(list
[(list
.size()-1)/2]);
990 myPlaylist
->Items
->Refresh();
991 wFooter
->ReadKey(input
);
993 Playlist::BlockNowPlayingUpdate
= 0;
994 Mpd
.StartCommandsList();
995 for (int i
= list
.size()-1; i
>= 0; --i
)
996 Mpd
.Move(origs
[i
], list
[i
]);
997 Mpd
.CommitCommandsList();
1002 from
= to
= myPlaylist
->Items
->Choice();
1003 bool modify_now_playing
= from
== size_t(myPlaylist
->NowPlaying
);
1004 if (modify_now_playing
)
1005 Playlist::BlockNowPlayingUpdate
= 1;
1006 while (Keypressed(input
, Key
.MvSongDown
) && to
< myPlaylist
->Items
->Size()-1)
1009 Playlist::BlockUpdate
= 1;
1010 myPlaylist
->UpdateTimer();
1011 if (modify_now_playing
)
1012 ++myPlaylist
->NowPlaying
;
1014 myPlaylist
->Items
->at(from
).SetPosition(to
);
1015 myPlaylist
->Items
->at(to
).SetPosition(from
);
1016 myPlaylist
->Items
->Swap(to
, to
-1);
1017 myPlaylist
->Items
->Scroll(wDown
);
1018 myPlaylist
->Items
->Refresh();
1019 wFooter
->ReadKey(input
);
1022 Playlist::BlockNowPlayingUpdate
= 0;
1023 UpdateStatusImmediately
= 1;
1025 wFooter
->SetTimeout(ncmpcpp_window_timeout
);
1028 else if (myScreen
->ActiveWindow() == myPlaylistEditor
->Content
&& !myPlaylistEditor
->Content
->Empty())
1030 wFooter
->SetTimeout(50);
1031 if (myPlaylistEditor
->Content
->hasSelected())
1033 std::vector
<size_t> list
;
1034 myPlaylistEditor
->Content
->GetSelected(list
);
1036 std::vector
<size_t> origs(list
);
1038 while (Keypressed(input
, Key
.MvSongDown
) && list
.back() < myPlaylistEditor
->Content
->Size()-1)
1041 myPlaylist
->UpdateTimer();
1042 for (std::vector
<size_t>::reverse_iterator it
= list
.rbegin(); it
!= list
.rend(); ++it
)
1045 myPlaylistEditor
->Content
->Swap(*it
, (*it
)-1);
1047 myPlaylistEditor
->Content
->Highlight(list
[(list
.size()-1)/2]);
1048 myPlaylistEditor
->Content
->Refresh();
1049 wFooter
->ReadKey(input
);
1051 Mpd
.StartCommandsList();
1052 for (int i
= list
.size()-1; i
>= 0; --i
)
1053 if (origs
[i
] != list
[i
])
1054 Mpd
.Move(myPlaylistEditor
->Playlists
->Current(), origs
[i
], list
[i
]);
1055 Mpd
.CommitCommandsList();
1060 from
= to
= myPlaylistEditor
->Content
->Choice();
1061 while (Keypressed(input
, Key
.MvSongDown
) && to
< myPlaylistEditor
->Content
->Size()-1)
1064 myPlaylist
->UpdateTimer();
1066 myPlaylistEditor
->Content
->Swap(to
, to
-1);
1067 myPlaylistEditor
->Content
->Scroll(wDown
);
1068 myPlaylistEditor
->Content
->Refresh();
1069 wFooter
->ReadKey(input
);
1072 Mpd
.Move(myPlaylistEditor
->Playlists
->Current(), from
, to
);
1074 wFooter
->SetTimeout(ncmpcpp_window_timeout
);
1077 else if (Keypressed(input
, Key
.MoveTo
) && myScreen
== myPlaylist
)
1079 CHECK_PLAYLIST_FOR_FILTERING
;
1080 if (!myPlaylist
->Items
->hasSelected())
1082 ShowMessage("No selected items to move!");
1085 Playlist::BlockUpdate
= 1;
1086 size_t pos
= myPlaylist
->Items
->Choice();
1087 // if cursor is at the last item, break convention and move at the end of playlist
1088 if (pos
== myPlaylist
->Items
->Size()-1)
1090 std::vector
<size_t> list
;
1091 myPlaylist
->Items
->GetSelected(list
);
1092 if (pos
>= list
.front() && pos
<= list
.back())
1094 int diff
= pos
-list
.front();
1095 Mpd
.StartCommandsList();
1099 size_t i
= list
.size()-1;
1100 for (std::vector
<size_t>::reverse_iterator it
= list
.rbegin(); it
!= list
.rend(); ++it
, --i
)
1102 Mpd
.Move(*it
, pos
+i
);
1103 myPlaylist
->Items
->Move(*it
, pos
+i
);
1109 for (std::vector
<size_t>::const_iterator it
= list
.begin(); it
!= list
.end(); ++it
, ++i
)
1111 Mpd
.Move(*it
, pos
+i
);
1112 myPlaylist
->Items
->Move(*it
, pos
+i
);
1115 Mpd
.CommitCommandsList();
1116 myPlaylist
->Items
->Highlight(pos
);
1117 myPlaylist
->FixPositions();
1119 else if (Keypressed(input
, Key
.Add
))
1121 if (myScreen
== myPlaylistEditor
&& myPlaylistEditor
->Playlists
->Empty())
1124 Statusbar() << (myScreen
== myPlaylistEditor
? "Add to playlist: " : "Add: ");
1125 std::string path
= wFooter
->GetString();
1129 if (myScreen
== myPlaylistEditor
)
1131 Mpd
.AddToPlaylist(myPlaylistEditor
->Playlists
->Current(), path
);
1132 myPlaylistEditor
->Content
->Clear(); // make it refetch content of playlist
1136 UpdateStatusImmediately
= 1;
1139 else if (Keypressed(input
, Key
.SeekForward
) || Keypressed(input
, Key
.SeekBackward
))
1141 if (!Mpd
.GetTotalTime())
1143 ShowMessage("Unknown item length!");
1147 const Song
*s
= myPlaylist
->NowPlayingSong();
1157 songpos
= Mpd
.GetElapsedTime();
1159 int old_timeout
= wFooter
->GetTimeout();
1160 wFooter
->SetTimeout(ncmpcpp_window_timeout
);
1162 SeekingInProgress
= 1;
1163 *wFooter
<< fmtBold
;
1164 while (Keypressed(input
, Key
.SeekForward
) || Keypressed(input
, Key
.SeekBackward
))
1167 myPlaylist
->UpdateTimer();
1168 wFooter
->ReadKey(input
);
1170 int howmuch
= Config
.incremental_seeking
? (myPlaylist
->Timer()-t
)/2+Config
.seek_time
: Config
.seek_time
;
1172 if (Keypressed(input
, Key
.SeekForward
) && songpos
< Mpd
.GetTotalTime())
1175 if (songpos
> Mpd
.GetTotalTime())
1176 songpos
= Mpd
.GetTotalTime();
1178 else if (Keypressed(input
, Key
.SeekBackward
) && songpos
> 0)
1185 std::string tracklength
;
1186 if (Config
.new_design
)
1188 if (Config
.display_remaining_time
)
1191 tracklength
+= Song::ShowTime(Mpd
.GetTotalTime()-songpos
);
1194 tracklength
= Song::ShowTime(songpos
);
1196 tracklength
+= MPD::Song::ShowTime(Mpd
.GetTotalTime());
1197 *wHeader
<< XY(0, 0) << tracklength
<< " ";
1203 if (Config
.display_remaining_time
)
1206 tracklength
+= Song::ShowTime(Mpd
.GetTotalTime()-songpos
);
1209 tracklength
+= Song::ShowTime(songpos
);
1211 tracklength
+= MPD::Song::ShowTime(Mpd
.GetTotalTime());
1213 *wFooter
<< XY(wFooter
->GetWidth()-tracklength
.length(), 1) << tracklength
;
1215 DrawProgressbar(songpos
, Mpd
.GetTotalTime());
1218 *wFooter
<< fmtBoldEnd
;
1219 SeekingInProgress
= 0;
1221 UpdateStatusImmediately
= 1;
1223 wFooter
->SetTimeout(old_timeout
);
1225 UnlockProgressbar();
1228 else if (Keypressed(input
, Key
.ToggleDisplayMode
))
1230 if (myScreen
== myPlaylist
)
1232 Config
.columns_in_playlist
= !Config
.columns_in_playlist
;
1233 ShowMessage("Playlist display mode: %s", Config
.columns_in_playlist
? "Columns" : "Classic");
1235 if (Config
.columns_in_playlist
)
1237 myPlaylist
->Items
->SetItemDisplayer(Display::SongsInColumns
);
1238 myPlaylist
->Items
->SetTitle(Display::Columns());
1239 myPlaylist
->Items
->SetGetStringFunction(Playlist::SongInColumnsToString
);
1243 myPlaylist
->Items
->SetItemDisplayer(Display::Songs
);
1244 myPlaylist
->Items
->SetTitle("");
1245 myPlaylist
->Items
->SetGetStringFunction(Playlist::SongToString
);
1248 else if (myScreen
== myBrowser
)
1250 Config
.columns_in_browser
= !Config
.columns_in_browser
;
1251 ShowMessage("Browser display mode: %s", Config
.columns_in_browser
? "Columns" : "Classic");
1252 myBrowser
->Main()->SetTitle(Config
.columns_in_browser
? Display::Columns() : "");
1255 else if (myScreen
== mySearcher
)
1257 Config
.columns_in_search_engine
= !Config
.columns_in_search_engine
;
1258 ShowMessage("Search engine display mode: %s", Config
.columns_in_search_engine
? "Columns" : "Classic");
1259 if (mySearcher
->Main()->Size() > SearchEngine::StaticOptions
)
1260 mySearcher
->Main()->SetTitle(Config
.columns_in_search_engine
? Display::Columns() : "");
1263 # ifdef HAVE_CURL_CURL_H
1264 else if (Keypressed(input
, Key
.ToggleLyricsDB
))
1266 const char *current_lyrics_plugin
= Lyrics::GetPluginName(++Config
.lyrics_db
);
1267 if (!current_lyrics_plugin
)
1269 current_lyrics_plugin
= Lyrics::GetPluginName(Config
.lyrics_db
= 0);
1271 ShowMessage("Using lyrics database: %s", current_lyrics_plugin
);
1273 # endif // HAVE_CURL_CURL_H
1274 else if (Keypressed(input
, Key
.ToggleAutoCenter
))
1276 Config
.autocenter_mode
= !Config
.autocenter_mode
;
1277 ShowMessage("Auto center mode: %s", Config
.autocenter_mode
? "On" : "Off");
1278 if (Config
.autocenter_mode
&& myPlaylist
->isPlaying() && !myPlaylist
->Items
->isFiltered())
1279 myPlaylist
->Items
->Highlight(myPlaylist
->NowPlaying
);
1281 else if (Keypressed(input
, Key
.UpdateDB
))
1283 if (myScreen
== myBrowser
)
1284 Mpd
.UpdateDirectory(locale_to_utf_cpy(myBrowser
->CurrentDir()));
1285 # ifdef HAVE_TAGLIB_H
1286 else if (myScreen
== myTagEditor
&& !Config
.albums_in_tag_editor
)
1287 Mpd
.UpdateDirectory(myTagEditor
->CurrentDir());
1288 # endif // HAVE_TAGLIB_H
1290 Mpd
.UpdateDirectory("/");
1292 else if (Keypressed(input
, Key
.GoToNowPlaying
))
1294 if (myScreen
== myPlaylist
&& myPlaylist
->isPlaying())
1296 CHECK_PLAYLIST_FOR_FILTERING
;
1297 myPlaylist
->Items
->Highlight(myPlaylist
->NowPlaying
);
1299 else if (myScreen
== myBrowser
)
1301 if (const Song
*s
= myPlaylist
->NowPlayingSong())
1303 myBrowser
->LocateSong(*s
);
1308 else if (Keypressed(input
, Key
.ToggleRepeat
))
1310 Mpd
.SetRepeat(!Mpd
.GetRepeat());
1311 UpdateStatusImmediately
= 1;
1313 else if (Keypressed(input
, Key
.Shuffle
))
1316 UpdateStatusImmediately
= 1;
1318 else if (Keypressed(input
, Key
.ToggleRandom
))
1320 Mpd
.SetRandom(!Mpd
.GetRandom());
1321 UpdateStatusImmediately
= 1;
1323 else if (Keypressed(input
, Key
.ToggleSingle
))
1325 if (myScreen
== mySearcher
&& !mySearcher
->Main()->isStatic(0))
1327 mySearcher
->Main()->Highlight(SearchEngine::SearchButton
);
1328 mySearcher
->Main()->Highlighting(0);
1329 mySearcher
->Main()->Refresh();
1330 mySearcher
->Main()->Highlighting(1);
1331 mySearcher
->EnterPressed();
1333 # ifdef HAVE_TAGLIB_H
1334 else if (myScreen
== myTinyTagEditor
)
1336 myTinyTagEditor
->Main()->Highlight(myTinyTagEditor
->Main()->Size()-2); // Save
1337 myTinyTagEditor
->EnterPressed();
1339 # endif // HAVE_TAGLIB_H
1342 Mpd
.SetSingle(!Mpd
.GetSingle());
1343 UpdateStatusImmediately
= 1;
1346 else if (Keypressed(input
, Key
.ToggleConsume
))
1348 Mpd
.SetConsume(!Mpd
.GetConsume());
1349 UpdateStatusImmediately
= 1;
1351 else if (Keypressed(input
, Key
.ToggleCrossfade
))
1353 Mpd
.SetCrossfade(Mpd
.GetCrossfade() ? 0 : Config
.crossfade_time
);
1354 UpdateStatusImmediately
= 1;
1356 else if (Keypressed(input
, Key
.SetCrossfade
))
1359 Statusbar() << "Set crossfade to: ";
1360 std::string crossfade
= wFooter
->GetString(3);
1362 int cf
= StrToInt(crossfade
);
1365 Config
.crossfade_time
= cf
;
1366 Mpd
.SetCrossfade(cf
);
1367 UpdateStatusImmediately
= 1;
1370 else if (Keypressed(input
, Key
.EditTags
))
1372 if ((myScreen
!= myBrowser
|| !myBrowser
->isLocal())
1373 && myScreen
!= myLyrics
)
1374 CHECK_MPD_MUSIC_DIR
;
1375 # ifdef HAVE_TAGLIB_H
1376 if (myTinyTagEditor
->SetEdited(myScreen
->CurrentSong()))
1378 myTinyTagEditor
->SwitchTo();
1380 else if (myScreen
->ActiveWindow() == myLibrary
->Artists
)
1383 Statusbar() << fmtBold
<< IntoStr(Config
.media_lib_primary_tag
) << fmtBoldEnd
<< ": ";
1384 std::string new_tag
= wFooter
->GetString(myLibrary
->Artists
->Current());
1386 if (!new_tag
.empty() && new_tag
!= myLibrary
->Artists
->Current())
1390 ShowMessage("Updating tags...");
1392 Mpd
.AddSearch(Config
.media_lib_primary_tag
, locale_to_utf_cpy(myLibrary
->Artists
->Current()));
1393 Mpd
.CommitSearch(list
);
1394 Song::SetFunction set
= IntoSetFunction(Config
.media_lib_primary_tag
);
1397 for (SongList::iterator it
= list
.begin(); it
!= list
.end(); ++it
)
1400 (*it
)->SetTags(set
, new_tag
);
1401 ShowMessage("Updating tags in \"%s\"...", (*it
)->GetName().c_str());
1402 std::string path
= Config
.mpd_music_dir
+ (*it
)->GetFile();
1403 if (!TagEditor::WriteTags(**it
))
1405 static const char msg
[] = "Error while updating tags in \"%s\"!";
1406 ShowMessage(msg
, Shorten(TO_WSTRING((*it
)->GetFile()), COLS
-static_strlen(msg
)).c_str());
1413 Mpd
.UpdateDirectory(locale_to_utf_cpy(FindSharedDir(list
)));
1414 ShowMessage("Tags updated succesfully!");
1419 else if (myScreen
->ActiveWindow() == myLibrary
->Albums
)
1422 Statusbar() << fmtBold
<< "Album: " << fmtBoldEnd
;
1423 std::string new_album
= wFooter
->GetString(myLibrary
->Albums
->Current().Album
);
1425 if (!new_album
.empty() && new_album
!= myLibrary
->Albums
->Current().Album
)
1428 ShowMessage("Updating tags...");
1429 for (size_t i
= 0; i
< myLibrary
->Songs
->Size(); ++i
)
1431 (*myLibrary
->Songs
)[i
].Localize();
1432 ShowMessage("Updating tags in \"%s\"...", (*myLibrary
->Songs
)[i
].GetName().c_str());
1433 std::string path
= Config
.mpd_music_dir
+ (*myLibrary
->Songs
)[i
].GetFile();
1434 TagLib::FileRef
f(locale_to_utf_cpy(path
).c_str());
1437 static const char msg
[] = "Error while opening file \"%s\"!";
1438 ShowMessage(msg
, Shorten(TO_WSTRING((*myLibrary
->Songs
)[i
].GetFile()), COLS
-static_strlen(msg
)).c_str());
1442 f
.tag()->setAlbum(ToWString(new_album
));
1445 static const char msg
[] = "Error while writing tags in \"%s\"!";
1446 ShowMessage(msg
, Shorten(TO_WSTRING((*myLibrary
->Songs
)[i
].GetFile()), COLS
-static_strlen(msg
)).c_str());
1453 Mpd
.UpdateDirectory(locale_to_utf_cpy(FindSharedDir(myLibrary
->Songs
)));
1454 ShowMessage("Tags updated succesfully!");
1458 else if (myScreen
->ActiveWindow() == myTagEditor
->Dirs
)
1460 std::string old_dir
= myTagEditor
->Dirs
->Current().first
;
1462 Statusbar() << fmtBold
<< "Directory: " << fmtBoldEnd
;
1463 std::string new_dir
= wFooter
->GetString(old_dir
);
1465 if (!new_dir
.empty() && new_dir
!= old_dir
)
1467 std::string full_old_dir
= Config
.mpd_music_dir
+ myTagEditor
->CurrentDir() + "/" + locale_to_utf_cpy(old_dir
);
1468 std::string full_new_dir
= Config
.mpd_music_dir
+ myTagEditor
->CurrentDir() + "/" + locale_to_utf_cpy(new_dir
);
1469 if (rename(full_old_dir
.c_str(), full_new_dir
.c_str()) == 0)
1471 static const char msg
[] = "Directory renamed to \"%s\"";
1472 ShowMessage(msg
, Shorten(TO_WSTRING(new_dir
), COLS
-static_strlen(msg
)).c_str());
1473 Mpd
.UpdateDirectory(myTagEditor
->CurrentDir());
1477 static const char msg
[] = "Couldn't rename \"%s\": %s";
1478 ShowMessage(msg
, Shorten(TO_WSTRING(old_dir
), COLS
-static_strlen(msg
)-25).c_str(), strerror(errno
));
1483 # endif // HAVE_TAGLIB_H
1484 if (myScreen
== myLyrics
)
1488 if (myScreen
== myBrowser
&& myBrowser
->Main()->Current().type
== itDirectory
)
1490 std::string old_dir
= myBrowser
->Main()->Current().name
;
1492 Statusbar() << fmtBold
<< "Directory: " << fmtBoldEnd
;
1493 std::string new_dir
= wFooter
->GetString(old_dir
);
1495 if (!new_dir
.empty() && new_dir
!= old_dir
)
1497 std::string full_old_dir
;
1498 if (!myBrowser
->isLocal())
1499 full_old_dir
+= Config
.mpd_music_dir
;
1500 full_old_dir
+= locale_to_utf_cpy(old_dir
);
1501 std::string full_new_dir
;
1502 if (!myBrowser
->isLocal())
1503 full_new_dir
+= Config
.mpd_music_dir
;
1504 full_new_dir
+= locale_to_utf_cpy(new_dir
);
1505 int rename_result
= rename(full_old_dir
.c_str(), full_new_dir
.c_str());
1506 if (rename_result
== 0)
1508 static const char msg
[] = "Directory renamed to \"%s\"";
1509 ShowMessage(msg
, Shorten(TO_WSTRING(new_dir
), COLS
-static_strlen(msg
)).c_str());
1510 if (!myBrowser
->isLocal())
1511 Mpd
.UpdateDirectory(locale_to_utf_cpy(FindSharedDir(old_dir
, new_dir
)));
1512 myBrowser
->GetDirectory(myBrowser
->CurrentDir());
1516 static const char msg
[] = "Couldn't rename \"%s\": %s";
1517 ShowMessage(msg
, Shorten(TO_WSTRING(old_dir
), COLS
-static_strlen(msg
)-25).c_str(), strerror(errno
));
1521 else if (myScreen
->ActiveWindow() == myPlaylistEditor
->Playlists
|| (myScreen
== myBrowser
&& myBrowser
->Main()->Current().type
== itPlaylist
))
1523 std::string old_name
= myScreen
->ActiveWindow() == myPlaylistEditor
->Playlists
? myPlaylistEditor
->Playlists
->Current() : myBrowser
->Main()->Current().name
;
1525 Statusbar() << fmtBold
<< "Playlist: " << fmtBoldEnd
;
1526 std::string new_name
= wFooter
->GetString(old_name
);
1528 if (!new_name
.empty() && new_name
!= old_name
)
1530 if (Mpd
.Rename(locale_to_utf_cpy(old_name
), locale_to_utf_cpy(new_name
)))
1532 static const char msg
[] = "Playlist renamed to \"%s\"";
1533 ShowMessage(msg
, Shorten(TO_WSTRING(new_name
), COLS
-static_strlen(msg
)).c_str());
1534 if (myBrowser
->Main() && !myBrowser
->isLocal())
1535 myBrowser
->GetDirectory("/");
1536 if (myPlaylistEditor
->Main())
1537 myPlaylistEditor
->Playlists
->Clear();
1542 else if (Keypressed(input
, Key
.GoToContainingDir
))
1544 Song
*s
= myScreen
->CurrentSong();
1546 myBrowser
->LocateSong(*s
);
1548 else if (Keypressed(input
, Key
.GoToMediaLibrary
))
1550 Song
*s
= myScreen
->CurrentSong();
1552 myLibrary
->LocateSong(*s
);
1554 else if (Keypressed(input
, Key
.GoToPosition
))
1556 if (!Mpd
.GetTotalTime())
1558 ShowMessage("Unknown item length!");
1562 const Song
*s
= myPlaylist
->NowPlayingSong();
1567 Statusbar() << "Position to go (in %/mm:ss/seconds(s)): ";
1568 std::string position
= wFooter
->GetString();
1571 if (position
.empty())
1575 if (position
.find(':') != std::string::npos
) // probably time in mm:ss
1577 newpos
= StrToInt(position
)*60 + StrToInt(position
.substr(position
.find(':')+1));
1578 if (newpos
>= 0 && newpos
<= Mpd
.GetTotalTime())
1581 ShowMessage("Out of bounds, 0:00-%s possible for mm:ss, %s given.", s
->GetLength().c_str(), MPD::Song::ShowTime(newpos
).c_str());
1583 else if (position
.find('s') != std::string::npos
) // probably position in seconds
1585 newpos
= StrToInt(position
);
1586 if (newpos
>= 0 && newpos
<= Mpd
.GetTotalTime())
1589 ShowMessage("Out of bounds, 0-%d possible for seconds, %d given.", s
->GetTotalLength(), newpos
);
1593 newpos
= StrToInt(position
);
1594 if (newpos
>= 0 && newpos
<= 100)
1595 Mpd
.Seek(Mpd
.GetTotalTime()*newpos
/100.0);
1597 ShowMessage("Out of bounds, 0-100 possible for %%, %d given.", newpos
);
1599 UpdateStatusImmediately
= 1;
1601 else if (Keypressed(input
, Key
.ReverseSelection
))
1603 if (myScreen
->allowsSelection())
1605 myScreen
->ReverseSelection();
1606 ShowMessage("Selection reversed!");
1609 else if (Keypressed(input
, Key
.DeselectAll
))
1611 if (myScreen
->allowsSelection())
1613 List
*mList
= myScreen
->GetList();
1614 if (!mList
->hasSelected())
1616 for (size_t i
= 0; i
< mList
->Size(); ++i
)
1617 mList
->Select(i
, 0);
1618 ShowMessage("Items deselected!");
1621 else if (Keypressed(input
, Key
.AddSelected
))
1623 mySelectedItemsAdder
->SwitchTo();
1625 else if (Keypressed(input
, Key
.Crop
))
1627 CHECK_PLAYLIST_FOR_FILTERING
;
1628 if (myPlaylist
->Items
->hasSelected())
1630 Mpd
.StartCommandsList();
1631 for (int i
= myPlaylist
->Items
->Size()-1; i
>= 0; --i
)
1633 if (!myPlaylist
->Items
->isSelected(i
) && i
!= myPlaylist
->NowPlaying
)
1636 // if mpd deletes now playing song deletion will be sluggishly slow
1637 // then so we have to assure it will be deleted at the very end.
1638 if (myPlaylist
->isPlaying() && !myPlaylist
->Items
->isSelected(myPlaylist
->NowPlaying
))
1639 Mpd
.DeleteID(myPlaylist
->NowPlayingSong()->GetID());
1641 ShowMessage("Deleting all items but selected...");
1642 Mpd
.CommitCommandsList();
1643 ShowMessage("Items deleted!");
1647 if (!myPlaylist
->isPlaying())
1649 ShowMessage("Nothing is playing now!");
1652 Mpd
.StartCommandsList();
1653 for (int i
= myPlaylist
->Items
->Size()-1; i
>= 0; --i
)
1654 if (i
!= myPlaylist
->NowPlaying
)
1656 ShowMessage("Deleting all items except now playing one...");
1657 Mpd
.CommitCommandsList();
1658 ShowMessage("Items deleted!");
1661 else if (Keypressed(input
, Key
.Clear
))
1663 if (myScreen
== myPlaylistEditor
&& myPlaylistEditor
->Playlists
->Empty())
1666 if (myScreen
->ActiveWindow() == myPlaylistEditor
->Content
1667 || Config
.ask_before_clearing_main_playlist
)
1670 Statusbar() << "Do you really want to clear playlist";
1671 if (myScreen
->ActiveWindow() == myPlaylistEditor
->Content
)
1672 *wFooter
<< " \"" << myPlaylistEditor
->Playlists
->Current() << "\"";
1673 *wFooter
<< " ? [" << fmtBold
<< 'y' << fmtBoldEnd
<< '/' << fmtBold
<< 'n' << fmtBoldEnd
<< "] ";
1679 wFooter
->ReadKey(answer
);
1681 while (answer
!= 'y' && answer
!= 'n');
1685 ShowMessage("Aborted!");
1690 if (myPlaylist
->Items
->isFiltered())
1692 ShowMessage("Deleting filtered items...");
1693 Mpd
.StartCommandsList();
1694 for (int i
= myPlaylist
->Items
->Size()-1; i
>= 0; --i
)
1695 Mpd
.Delete((*myPlaylist
->Items
)[i
].GetPosition());
1696 Mpd
.CommitCommandsList();
1697 ShowMessage("Filtered items deleted!");
1701 if (myScreen
->ActiveWindow() == myPlaylistEditor
->Content
)
1703 Mpd
.ClearPlaylist(locale_to_utf_cpy(myPlaylistEditor
->Playlists
->Current()));
1704 myPlaylistEditor
->Content
->Clear();
1708 ShowMessage("Clearing playlist...");
1709 Mpd
.ClearPlaylist();
1712 // if playlist is cleared, items list have to be updated, but this
1713 // can be blocked if new song was added to playlist less than one
1714 // second ago, so we need to assume it's unlocked.
1715 BlockItemListUpdate
= 0;
1716 UpdateStatusImmediately
= 1;
1718 else if (Keypressed(input
, Key
.SortPlaylist
) && myScreen
== myPlaylist
)
1720 CHECK_PLAYLIST_FOR_FILTERING
;
1723 else if (Keypressed(input
, Key
.ApplyFilter
))
1725 List
*mList
= myScreen
->GetList();
1731 Statusbar() << fmtBold
<< "Apply filter: " << fmtBoldEnd
;
1732 wFooter
->SetGetStringHelper(StatusbarApplyFilterImmediately
);
1733 wFooter
->GetString(mList
->GetFilter());
1734 wFooter
->SetGetStringHelper(StatusbarGetStringHelper
);
1737 if (mList
->isFiltered())
1738 ShowMessage("Using filter \"%s\"", mList
->GetFilter().c_str());
1740 ShowMessage("Filtering disabled");
1742 if (myScreen
== myPlaylist
)
1744 myPlaylist
->EnableHighlighting();
1745 Playlist::ReloadTotalLength
= 1;
1749 else if (Keypressed(input
, Key
.FindForward
) || Keypressed(input
, Key
.FindBackward
))
1751 List
*mList
= myScreen
->GetList();
1756 Statusbar() << "Find " << (Keypressed(input
, Key
.FindForward
) ? "forward" : "backward") << ": ";
1757 std::string findme
= wFooter
->GetString();
1759 myPlaylist
->UpdateTimer();
1761 if (!findme
.empty())
1762 ShowMessage("Searching...");
1764 bool success
= mList
->Search(findme
, myScreen
== mySearcher
? SearchEngine::StaticOptions
: 0, REG_ICASE
| Config
.regex_type
);
1769 success
? ShowMessage("Searching finished!") : ShowMessage("Unable to find \"%s\"", findme
.c_str());
1771 if (Keypressed(input
, Key
.FindForward
))
1772 mList
->NextFound(Config
.wrapped_search
);
1774 mList
->PrevFound(Config
.wrapped_search
);
1776 if (myScreen
== myPlaylist
)
1777 myPlaylist
->EnableHighlighting();
1779 else if (myScreen
== myHelp
|| myScreen
== myLyrics
|| myScreen
== myInfo
)
1782 Statusbar() << "Find: ";
1783 std::string findme
= wFooter
->GetString();
1786 ShowMessage("Searching...");
1787 Screen
<Scrollpad
> *s
= static_cast<Screen
<Scrollpad
> *>(myScreen
);
1788 s
->Main()->RemoveFormatting();
1789 ShowMessage("%s", findme
.empty() || s
->Main()->SetFormatting(fmtReverse
, TO_WSTRING(findme
), fmtReverseEnd
, 0) ? "Done!" : "No matching patterns found");
1793 else if (Keypressed(input
, Key
.NextFoundPosition
) || Keypressed(input
, Key
.PrevFoundPosition
))
1795 List
*mList
= myScreen
->GetList();
1800 if (Keypressed(input
, Key
.NextFoundPosition
))
1801 mList
->NextFound(Config
.wrapped_search
);
1803 mList
->PrevFound(Config
.wrapped_search
);
1805 else if (Keypressed(input
, Key
.ToggleFindMode
))
1807 Config
.wrapped_search
= !Config
.wrapped_search
;
1808 ShowMessage("Search mode: %s", Config
.wrapped_search
? "Wrapped" : "Normal");
1810 else if (Keypressed(input
, Key
.ToggleReplayGainMode
) && Mpd
.Version() >= 16)
1813 Statusbar() << "Replay gain mode ? [" << fmtBold
<< 'o' << fmtBoldEnd
<< "ff/" << fmtBold
<< 't' << fmtBoldEnd
<< "rack/" << fmtBold
<< 'a' << fmtBoldEnd
<< "lbum]";
1819 wFooter
->ReadKey(answer
);
1821 while (answer
!= 'o' && answer
!= 't' && answer
!= 'a');
1823 Mpd
.SetReplayGainMode(answer
== 't' ? rgmTrack
: (answer
== 'a' ? rgmAlbum
: rgmOff
));
1824 ShowMessage("Replay gain mode: %s", Mpd
.GetReplayGainMode().c_str());
1826 else if (Keypressed(input
, Key
.ToggleSpaceMode
))
1828 Config
.space_selects
= !Config
.space_selects
;
1829 ShowMessage("Space mode: %s item", Config
.space_selects
? "Select/deselect" : "Add");
1831 else if (Keypressed(input
, Key
.ToggleAddMode
))
1833 Config
.ncmpc_like_songs_adding
= !Config
.ncmpc_like_songs_adding
;
1834 ShowMessage("Add mode: %s", Config
.ncmpc_like_songs_adding
? "Add item to playlist, remove if already added" : "Always add item to playlist");
1836 else if (Keypressed(input
, Key
.ToggleMouse
))
1838 Config
.mouse_support
= !Config
.mouse_support
;
1839 mousemask(Config
.mouse_support
? ALL_MOUSE_EVENTS
: 0, 0);
1840 ShowMessage("Mouse support %s", Config
.mouse_support
? "enabled" : "disabled");
1842 else if (Keypressed(input
, Key
.SwitchTagTypeList
))
1844 if (myScreen
== myPlaylist
)
1847 Statusbar() << "Add random ? [" << fmtBold
<< 's' << fmtBoldEnd
<< "ongs/" << fmtBold
<< 'a' << fmtBoldEnd
<< "rtists/al" << fmtBold
<< 'b' << fmtBoldEnd
<< "ums] ";
1853 wFooter
->ReadKey(answer
);
1855 while (answer
!= 's' && answer
!= 'a' && answer
!= 'b');
1858 mpd_tag_type tag_type
= IntoTagItem(answer
);
1859 std::string tag_type_str
= answer
== 's' ? "song" : IntoStr(tag_type
);
1860 ToLower(tag_type_str
);
1863 Statusbar() << "Number of random " << tag_type_str
<< "s: ";
1864 size_t number
= StrToLong(wFooter
->GetString());
1866 if (number
&& (answer
== 's' ? Mpd
.AddRandomSongs(number
) : Mpd
.AddRandomTag(tag_type
, number
)))
1867 ShowMessage("%zu random %s%s added to playlist!", number
, tag_type_str
.c_str(), number
== 1 ? "" : "s");
1869 else if (myScreen
== myBrowser
&& !myBrowser
->isLocal())
1871 Config
.browser_sort_by_mtime
= !Config
.browser_sort_by_mtime
;
1872 myBrowser
->Main()->Sort
<CaseInsensitiveSorting
>(myBrowser
->CurrentDir() != "/");
1873 ShowMessage("Sort songs by: %s", Config
.browser_sort_by_mtime
? "Modification time" : "Name");
1875 else if (myScreen
->ActiveWindow() == myLibrary
->Artists
1876 || (myLibrary
->Columns() == 2 && myScreen
->ActiveWindow() == myLibrary
->Albums
))
1879 Statusbar() << "Tag type ? [" << fmtBold
<< 'a' << fmtBoldEnd
<< "rtist/" << fmtBold
<< 'y' << fmtBoldEnd
<< "ear/" << fmtBold
<< 'g' << fmtBoldEnd
<< "enre/" << fmtBold
<< 'c' << fmtBoldEnd
<< "omposer/" << fmtBold
<< 'p' << fmtBoldEnd
<< "erformer] ";
1885 wFooter
->ReadKey(answer
);
1887 while (answer
!= 'a' && answer
!= 'y' && answer
!= 'g' && answer
!= 'c' && answer
!= 'p');
1889 mpd_tag_type new_tagitem
= IntoTagItem(answer
);
1890 if (new_tagitem
!= Config
.media_lib_primary_tag
)
1892 Config
.media_lib_primary_tag
= new_tagitem
;
1893 std::string item_type
= IntoStr(Config
.media_lib_primary_tag
);
1894 myLibrary
->Artists
->SetTitle(item_type
+ "s");
1895 myLibrary
->Artists
->Reset();
1897 if (myLibrary
->Columns() == 2)
1899 myLibrary
->Songs
->Clear();
1900 myLibrary
->Albums
->Reset();
1901 myLibrary
->Albums
->Clear();
1902 myLibrary
->Albums
->SetTitle("Albums (sorted by " + item_type
+ ")");
1903 myLibrary
->Albums
->Display();
1907 myLibrary
->Artists
->Clear();
1908 myLibrary
->Artists
->Display();
1910 ShowMessage("Switched to list of %s tag", item_type
.c_str());
1913 else if (myScreen
== myLyrics
)
1915 myLyrics
->FetchAgain();
1918 else if (Keypressed(input
, Key
.SongInfo
))
1922 # ifdef HAVE_CURL_CURL_H
1923 else if (Keypressed(input
, Key
.ArtistInfo
))
1925 myInfo
->GetArtist();
1927 # endif // HAVE_CURL_CURL_H
1928 else if (Keypressed(input
, Key
.Lyrics
))
1930 myLyrics
->SwitchTo();
1932 else if (Keypressed(input
, Key
.Quit
))
1936 # ifdef HAVE_TAGLIB_H
1937 else if (myScreen
== myTinyTagEditor
)
1941 # endif // HAVE_TAGLIB_H
1942 else if (Keypressed(input
, Key
.Help
))
1946 else if (Keypressed(input
, Key
.ScreenSwitcher
))
1948 if (Config
.screen_switcher_browser_only
)
1950 if (myScreen
== myPlaylist
)
1951 myBrowser
->SwitchTo();
1953 myPlaylist
->SwitchTo();
1957 if (myScreen
->isTabbable())
1958 myPrevScreen
->SwitchTo();
1960 myOldScreen
->SwitchTo();
1963 else if (Keypressed(input
, Key
.Playlist
))
1965 myPlaylist
->SwitchTo();
1967 else if (Keypressed(input
, Key
.Browser
))
1969 myBrowser
->SwitchTo();
1971 else if (Keypressed(input
, Key
.SearchEngine
))
1973 mySearcher
->SwitchTo();
1975 else if (Keypressed(input
, Key
.MediaLibrary
))
1977 myLibrary
->SwitchTo();
1979 else if (Keypressed(input
, Key
.PlaylistEditor
))
1981 myPlaylistEditor
->SwitchTo();
1983 # ifdef HAVE_TAGLIB_H
1984 else if (Keypressed(input
, Key
.TagEditor
))
1986 CHECK_MPD_MUSIC_DIR
;
1987 myTagEditor
->SwitchTo();
1989 # endif // HAVE_TAGLIB_H
1990 # ifdef ENABLE_OUTPUTS
1991 else if (Keypressed(input
, Key
.Outputs
))
1993 myOutputs
->SwitchTo();
1995 # endif // ENABLE_OUTPUTS
1996 # ifdef ENABLE_VISUALIZER
1997 else if (Keypressed(input
, Key
.Visualizer
))
1999 myVisualizer
->SwitchTo();
2001 # endif // ENABLE_VISUALIZER
2002 # ifdef ENABLE_CLOCK
2003 else if (Keypressed(input
, Key
.Clock
))
2005 myClock
->SwitchTo();
2007 # endif // ENABLE_CLOCK
2008 else if (Keypressed(input
, Key
.ServerInfo
))
2010 myServerInfo
->SwitchTo();
2014 if (myScreen
== myLibrary
2015 || myScreen
== myPlaylistEditor
2016 # ifdef HAVE_TAGLIB_H
2017 || myScreen
== myTagEditor
2018 # endif // HAVE_TAGLIB_H
2021 if (Keypressed(input
, Key
.Up
)
2022 || Keypressed(input
, Key
.Down
)
2023 || Keypressed(input
, Key
.PageUp
)
2024 || Keypressed(input
, Key
.PageDown
)
2025 || Keypressed(input
, Key
.Home
)
2026 || Keypressed(input
, Key
.End
)
2027 || Keypressed(input
, Key
.ApplyFilter
)
2028 || Keypressed(input
, Key
.FindForward
)
2029 || Keypressed(input
, Key
.FindBackward
)
2030 || Keypressed(input
, Key
.NextFoundPosition
)
2031 || Keypressed(input
, Key
.PrevFoundPosition
))
2033 if (myScreen
->ActiveWindow() == myLibrary
->Artists
)
2035 myLibrary
->Albums
->Clear();
2037 else if (myScreen
->ActiveWindow() == myLibrary
->Albums
)
2039 myLibrary
->Songs
->Clear();
2041 else if (myScreen
->ActiveWindow() == myPlaylistEditor
->Playlists
)
2043 myPlaylistEditor
->Content
->Clear();
2045 # ifdef HAVE_TAGLIB_H
2046 else if (myScreen
->ActiveWindow() == myTagEditor
->LeftColumn
)
2048 myTagEditor
->Tags
->Clear();
2050 # endif // HAVE_TAGLIB_H
2054 # ifdef ENABLE_VISUALIZER
2055 // visualizer sets timmeout to 40ms, but since only it needs such small
2056 // value, we should restore defalt one after switching to another screen.
2057 if (wFooter
->GetTimeout() < ncmpcpp_window_timeout
&& myScreen
!= myVisualizer
)
2058 wFooter
->SetTimeout(ncmpcpp_window_timeout
);
2059 # endif // ENABLE_VISUALIZER