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