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