Files
ncmpcpp/src/ncmpcpp.cpp
Andrzej Rybczak 30b8622776 get total time from mpd status, not from currently playing track
it seems that even if track info doesn't provide total track length,
mpd status does, which allows for seeking and displaying progressbar.
2009-09-09 18:32:57 +02:00

1904 lines
57 KiB
C++

/***************************************************************************
* Copyright (C) 2008-2009 by Andrzej Rybczak *
* electricityispower@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#include <cerrno>
#include <clocale>
#include <csignal>
#include <cstring>
#include <iostream>
#include <fstream>
#include <stdexcept>
#include "mpdpp.h"
#include "ncmpcpp.h"
#include "browser.h"
#include "charset.h"
#include "clock.h"
#include "display.h"
#include "global.h"
#include "help.h"
#include "helpers.h"
#include "media_library.h"
#include "misc.h"
#include "lyrics.h"
#include "playlist.h"
#include "playlist_editor.h"
#include "search_engine.h"
#include "settings.h"
#include "song.h"
#include "info.h"
#include "outputs.h"
#include "status.h"
#include "tag_editor.h"
#include "tiny_tag_editor.h"
#include "visualizer.h"
#define CHECK_PLAYLIST_FOR_FILTERING \
if (myPlaylist->Items->isFiltered()) \
{ \
ShowMessage("%s", MPD::Message::FunctionDisabledFilteringEnabled); \
continue; \
}
#define CHECK_MPD_MUSIC_DIR \
if (Config.mpd_music_dir.empty()) \
{ \
ShowMessage("configuration variable mpd_music_dir is not set!"); \
continue; \
}
using namespace Global;
using namespace MPD;
BasicScreen *Global::myScreen;
BasicScreen *Global::myOldScreen;
Window *Global::wHeader;
Window *Global::wFooter;
size_t Global::MainStartY;
size_t Global::MainHeight;
bool Global::BlockItemListUpdate = 0;
bool Global::MessagesAllowed = 0;
bool Global::SeekingInProgress = 0;
bool Global::RedrawHeader = 1;
namespace
{
std::ofstream errorlog;
std::streambuf *cerr_buffer;
void do_at_exit()
{
// restore old cerr buffer
std::cerr.rdbuf(cerr_buffer);
errorlog.close();
Mpd.Disconnect();
# ifndef USE_PDCURSES // destroying screen somehow crashes pdcurses
DestroyScreen();
# endif // USE_PDCURSES
WindowTitle("");
}
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL, "");
CreateConfigDir();
DefaultConfiguration(Config);
DefaultKeys(Key);
ReadConfiguration(Config);
ReadKeys(Key);
if (getenv("MPD_HOST"))
Mpd.SetHostname(getenv("MPD_HOST"));
if (getenv("MPD_PORT"))
Mpd.SetPort(atoi(getenv("MPD_PORT")));
if (Config.mpd_host != "localhost")
Mpd.SetHostname(Config.mpd_host);
if (Config.mpd_port != 6600)
Mpd.SetPort(Config.mpd_port);
Mpd.SetTimeout(Config.mpd_connection_timeout);
if (argc > 1)
ParseArgv(argc, argv);
if (!ConnectToMPD())
return -1;
// always execute these commands, even if ncmpcpp use exit function
atexit(do_at_exit);
// redirect std::cerr output to ~/.ncmpcpp/error.log file
errorlog.open((config_dir + "error.log").c_str(), std::ios::app);
cerr_buffer = std::cerr.rdbuf();
std::cerr.rdbuf(errorlog.rdbuf());
InitScreen("ncmpc++ ver. "VERSION, Config.colors_enabled);
bool real_statusbar_visibility = Config.statusbar_visibility;
if (Config.new_design)
Config.statusbar_visibility = 0;
size_t header_height, footer_start_y, footer_height;
SetWindowsDimensions(header_height, footer_start_y, footer_height);
if (Config.header_visibility || Config.new_design)
{
wHeader = new Window(0, 0, COLS, header_height, "", Config.header_color, brNone);
wHeader->Display();
}
wFooter = new Window(0, footer_start_y, COLS, footer_height, "", Config.statusbar_color, brNone);
wFooter->SetTimeout(ncmpcpp_window_timeout);
wFooter->SetGetStringHelper(StatusbarGetStringHelper);
wFooter->CreateHistory();
myPlaylist->SwitchTo();
myPlaylist->UpdateTimer();
Mpd.SetStatusUpdater(NcmpcppStatusChanged, 0);
Mpd.SetErrorHandler(NcmpcppErrorCallback, 0);
// local variables
int input;
bool main_exit = 0;
bool design_changed = 0;
bool title_allowed = !Config.display_screens_numbers_on_start;
std::string screen_title;
timeval now, past;
// local variables end
# ifndef WIN32
signal(SIGPIPE, SIG_IGN);
# endif // !WIN32
gettimeofday(&now, 0);
MEVENT mouse_event;
mouseinterval(0);
if (Config.mouse_support)
mousemask(ALL_MOUSE_EVENTS, 0);
if (Config.jump_to_now_playing_song_at_start)
{
TraceMpdStatus();
if (myPlaylist->isPlaying())
myPlaylist->Items->Highlight(myPlaylist->NowPlaying);
}
while (!main_exit)
{
if (!Mpd.Connected())
{
ShowMessage("Attempting to reconnect...");
if (Mpd.Connect())
{
ShowMessage("Connected to %s!", Mpd.GetHostname().c_str());
MessagesAllowed = 0;
UpdateStatusImmediately = 1;
# ifdef ENABLE_OUTPUTS
myOutputs->FetchList();
# endif // ENABLE_OUTPUTS
# ifdef ENABLE_VISUALIZER
myVisualizer->ResetFD();
if (myScreen == myVisualizer)
myVisualizer->SetFD();
# endif // ENABLE_VISUALIZER
}
}
TraceMpdStatus();
MessagesAllowed = 1;
// header stuff
gettimeofday(&past, 0);
if (((past.tv_sec == now.tv_sec && past.tv_usec >= now.tv_usec+500000) || past.tv_sec > now.tv_sec)
&& (myScreen == myPlaylist || myScreen == myBrowser || myScreen == myLyrics)
)
{
RedrawHeader = 1;
gettimeofday(&now, 0);
}
if (Config.header_visibility && RedrawHeader)
{
if (title_allowed)
{
if (Config.new_design)
{
std::basic_string<my_char_t> title = myScreen->Title();
*wHeader << XY(0, 3) << wclrtoeol;
*wHeader << fmtBold << Config.alternative_ui_separator_color;
mvwhline(wHeader->Raw(), 2, 0, 0, COLS);
mvwhline(wHeader->Raw(), 4, 0, 0, COLS);
*wHeader << XY((COLS-Window::Length(title))/2, 3);
*wHeader << Config.header_color << title << clEnd;
*wHeader << clEnd << fmtBoldEnd;
}
else
*wHeader << XY(0, 0) << wclrtoeol << fmtBold << myScreen->Title() << fmtBoldEnd;
}
else
{
*wHeader << XY(0, Config.new_design ? 3 : 0)
<< fmtBold << char(Key.Help[0]) << fmtBoldEnd << ":Help "
<< fmtBold << char(Key.Playlist[0]) << fmtBoldEnd << ":Playlist "
<< fmtBold << char(Key.Browser[0]) << fmtBoldEnd << ":Browse "
<< fmtBold << char(Key.SearchEngine[0]) << fmtBoldEnd << ":Search "
<< fmtBold << char(Key.MediaLibrary[0]) << fmtBoldEnd << ":Library "
<< fmtBold << char(Key.PlaylistEditor[0]) << fmtBoldEnd << ":Playlist editor";
# ifdef HAVE_TAGLIB_H
*wHeader << " " << fmtBold << char(Key.TagEditor[0]) << fmtBoldEnd << ":Tag editor";
# endif // HAVE_TAGLIB_H
# ifdef ENABLE_VISUALIZER
*wHeader << " " << fmtBold << char(Key.Visualizer[0]) << fmtBoldEnd << ":Music visualizer";
# endif // ENABLE_VISUALIZER
# ifdef ENABLE_CLOCK
*wHeader << " " << fmtBold << char(Key.Clock[0]) << fmtBoldEnd << ":Clock";
# endif // ENABLE_CLOCK
if (Config.new_design)
{
*wHeader << fmtBold << Config.alternative_ui_separator_color;
mvwhline(wHeader->Raw(), 2, 0, 0, COLS);
mvwhline(wHeader->Raw(), 4, 0, 0, COLS);
*wHeader << clEnd << fmtBoldEnd;
}
}
if (!Config.new_design)
{
*wHeader << Config.volume_color;
*wHeader << XY(wHeader->GetWidth()-VolumeState.length(), 0) << VolumeState;
*wHeader << clEnd;
}
wHeader->Refresh();
RedrawHeader = 0;
}
// header stuff end
myScreen->Update();
myScreen->RefreshWindow();
myScreen->ReadKey(input);
if (input == ERR)
continue;
if (!title_allowed)
RedrawHeader = 1;
title_allowed = 1;
if (myScreen == myPlaylist)
myPlaylist->EnableHighlighting();
else if (
myScreen == myLibrary
|| myScreen == myPlaylistEditor
# ifdef HAVE_TAGLIB_H
|| myScreen == myTagEditor
# endif // HAVE_TAGLIB_H
)
{
if (Keypressed(input, Key.Up)
|| Keypressed(input, Key.Down)
|| Keypressed(input, Key.PageUp)
|| Keypressed(input, Key.PageDown)
|| Keypressed(input, Key.Home)
|| Keypressed(input, Key.End)
|| Keypressed(input, Key.ApplyFilter)
|| Keypressed(input, Key.FindForward)
|| Keypressed(input, Key.FindBackward)
|| Keypressed(input, Key.NextFoundPosition)
|| Keypressed(input, Key.PrevFoundPosition))
{
if (myScreen->ActiveWindow() == myLibrary->Artists)
{
myLibrary->Albums->Clear(0);
myLibrary->Songs->Clear(0);
}
else if (myScreen->ActiveWindow() == myLibrary->Albums)
{
myLibrary->Songs->Clear(0);
}
else if (myScreen->ActiveWindow() == myPlaylistEditor->Playlists)
{
myPlaylistEditor->Content->Clear(0);
}
# ifdef HAVE_TAGLIB_H
else if (myScreen->ActiveWindow() == myTagEditor->LeftColumn)
{
myTagEditor->Tags->Clear(0);
myTagEditor->TagTypes->Refresh();
}
# endif // HAVE_TAGLIB_H
}
}
// key mapping beginning
if (Keypressed(input, Key.ToggleInterface))
{
Config.new_design = !Config.new_design;
Config.statusbar_visibility = Config.new_design ? 0 : real_statusbar_visibility;
SetWindowsDimensions(header_height, footer_start_y, footer_height);
UnlockProgressbar();
UnlockStatusbar();
design_changed = 1;
}
if (Keypressed(input, Key.Up))
{
myScreen->Scroll(wUp, Key.Up);
}
else if (Keypressed(input, Key.Down))
{
myScreen->Scroll(wDown, Key.Down);
}
else if (Keypressed(input, Key.PageUp))
{
myScreen->Scroll(wPageUp, Key.PageUp);
}
else if (Keypressed(input, Key.PageDown))
{
myScreen->Scroll(wPageDown, Key.PageDown);
}
else if (Keypressed(input, Key.Home))
{
myScreen->Scroll(wHome);
}
else if (Keypressed(input, Key.End))
{
myScreen->Scroll(wEnd);
}
else if (Config.mouse_support && input == KEY_MOUSE)
{
# ifdef USE_PDCURSES
nc_getmouse(&mouse_event);
# else
getmouse(&mouse_event);
# endif // USE_PDCURSES
if (mouse_event.bstate & BUTTON1_PRESSED
&& mouse_event.y == LINES-(Config.statusbar_visibility ? 2 : 1)
) // progressbar
{
if (!myPlaylist->isPlaying())
continue;
Mpd.Seek(Mpd.GetTotalTime()*mouse_event.x/double(COLS));
UpdateStatusImmediately = 1;
}
else if (mouse_event.bstate & BUTTON1_PRESSED
&& (Config.statusbar_visibility || Config.new_design)
&& Mpd.GetState() > psStop
&& mouse_event.y == (Config.new_design ? 1 : LINES-1) && mouse_event.x < 9
) // playing/paused
{
Mpd.Pause();
UpdateStatusImmediately = 1;
}
else if ((mouse_event.bstate & BUTTON2_PRESSED || mouse_event.bstate & BUTTON4_PRESSED)
&& Config.header_visibility
&& mouse_event.y == 0 && size_t(mouse_event.x) > COLS-VolumeState.length()
) // volume
{
if (mouse_event.bstate & BUTTON2_PRESSED)
Mpd.SetVolume(Mpd.GetVolume()-2);
else
Mpd.SetVolume(Mpd.GetVolume()+2);
}
else
myScreen->MouseButtonPressed(mouse_event);
}
else if (input == KEY_RESIZE || design_changed)
{
# ifdef USE_PDCURSES
resize_term(0, 0);
# endif // USE_PDCURSES
RedrawHeader = 1;
MainHeight = LINES-(Config.new_design ? 7 : 4);
if (COLS < 20 || MainHeight < 3)
{
DestroyScreen();
std::cout << "Screen is too small!\n";
exit(1);
}
if (!Config.header_visibility)
MainHeight += 2;
if (!Config.statusbar_visibility)
MainHeight++;
myHelp->hasToBeResized = 1;
myPlaylist->hasToBeResized = 1;
myBrowser->hasToBeResized = 1;
mySearcher->hasToBeResized = 1;
myLibrary->hasToBeResized = 1;
myPlaylistEditor->hasToBeResized = 1;
myInfo->hasToBeResized = 1;
myLyrics->hasToBeResized = 1;
mySelectedItemsAdder->hasToBeResized = 1;
# ifdef HAVE_TAGLIB_H
myTinyTagEditor->hasToBeResized = 1;
myTagEditor->hasToBeResized = 1;
# endif // HAVE_TAGLIB_H
# ifdef ENABLE_VISUALIZER
myVisualizer->hasToBeResized = 1;
# endif // ENABLE_VISUALIZER
# ifdef ENABLE_OUTPUTS
myOutputs->hasToBeResized = 1;
# endif // ENABLE_OUTPUTS
# ifdef ENABLE_CLOCK
myClock->hasToBeResized = 1;
# endif // ENABLE_CLOCK
myScreen->Resize();
if (Config.header_visibility || Config.new_design)
wHeader->Resize(COLS, header_height);
footer_start_y = LINES-(Config.statusbar_visibility ? 2 : 1);
wFooter->MoveTo(0, footer_start_y);
wFooter->Resize(COLS, Config.statusbar_visibility ? 2 : 1);
myScreen->Refresh();
RedrawStatusbar = 1;
StatusChanges changes;
if (Mpd.GetState() < psPlay || design_changed)
{
changes.PlayerState = 1;
if (design_changed)
changes.Volume = 1;
}
// Note: routines for drawing separator if alternative user
// interface is active and header is hidden are placed in
// NcmpcppStatusChanges.StatusFlags
changes.StatusFlags = 1; // force status update
NcmpcppStatusChanged(&Mpd, changes, 0);
if (design_changed)
{
RedrawStatusbar = 1;
NcmpcppStatusChanged(&Mpd, StatusChanges(), 0);
design_changed = 0;
ShowMessage("User interface: %s", Config.new_design ? "Alternative" : "Classic");
}
}
else if (Keypressed(input, Key.GoToParentDir))
{
if (myScreen == myBrowser && myBrowser->CurrentDir() != "/")
{
myBrowser->Main()->Reset();
myBrowser->EnterPressed();
}
}
else if (Keypressed(input, Key.Enter))
{
myScreen->EnterPressed();
}
else if (Keypressed(input, Key.Space))
{
myScreen->SpacePressed();
}
else if (Keypressed(input, Key.VolumeUp))
{
if (myScreen == myLibrary && input == Key.VolumeUp[0])
{
myLibrary->NextColumn();
}
else if (myScreen == myPlaylistEditor && input == Key.VolumeUp[0])
{
myPlaylistEditor->NextColumn();
}
# ifdef HAVE_TAGLIB_H
else if (myScreen == myTagEditor && input == Key.VolumeUp[0])
{
myTagEditor->NextColumn();
}
# endif // HAVE_TAGLIB_H
else
Mpd.SetVolume(Mpd.GetVolume()+1);
}
else if (Keypressed(input, Key.VolumeDown))
{
if (myScreen == myLibrary && input == Key.VolumeDown[0])
{
myLibrary->PrevColumn();
}
else if (myScreen == myPlaylistEditor && input == Key.VolumeDown[0])
{
myPlaylistEditor->PrevColumn();
}
# ifdef HAVE_TAGLIB_H
else if (myScreen == myTagEditor && input == Key.VolumeDown[0])
{
myTagEditor->PrevColumn();
}
# endif // HAVE_TAGLIB_H
else
Mpd.SetVolume(Mpd.GetVolume()-1);
}
else if (Keypressed(input, Key.Delete))
{
if (!myPlaylist->Items->Empty() && myScreen == myPlaylist)
{
Playlist::BlockUpdate = 1;
if (myPlaylist->Items->hasSelected())
{
std::vector<size_t> list;
myPlaylist->Items->GetSelected(list);
Mpd.StartCommandsList();
for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it)
{
Mpd.DeleteID((*myPlaylist->Items)[*it].GetID());
myPlaylist->Items->DeleteOption(*it);
}
Mpd.CommitCommandsList();
myPlaylist->FixPositions(list.front());
ShowMessage("Selected items deleted!");
}
else
{
Playlist::BlockNowPlayingUpdate = 1;
myPlaylist->Items->SetTimeout(50);
int del_counter = 0;
while (!myPlaylist->Items->Empty() && Keypressed(input, Key.Delete))
{
size_t id = myPlaylist->Items->Choice();
TraceMpdStatus();
Playlist::BlockUpdate = 1;
myPlaylist->UpdateTimer();
// needed for keeping proper position of now playing song.
if (myPlaylist->NowPlaying > myPlaylist->CurrentSong()->GetPosition()-del_counter)
myPlaylist->NowPlaying--;
Mpd.DeleteID(myPlaylist->CurrentSong()->GetID());
myPlaylist->Items->DeleteOption(id);
myPlaylist->Items->Refresh();
myPlaylist->Items->ReadKey(input);
del_counter++;
}
myPlaylist->FixPositions(myPlaylist->Items->Choice());
myPlaylist->Items->SetTimeout(ncmpcpp_window_timeout);
Playlist::BlockNowPlayingUpdate = 0;
}
}
else if (
(myScreen == myBrowser && !myBrowser->Main()->Empty() && myBrowser->Main()->Current().type == itPlaylist)
|| (myScreen->ActiveWindow() == myPlaylistEditor->Playlists)
)
{
LockStatusbar();
std::string name = myScreen == myBrowser ? myBrowser->Main()->Current().name : myPlaylistEditor->Playlists->Current();
Statusbar() << "Delete playlist \"" << name << "\" ? [y/n] ";
curs_set(1);
int in = 0;
do
{
TraceMpdStatus();
wFooter->ReadKey(in);
}
while (in != 'y' && in != 'n');
if (in == 'y')
{
Mpd.DeletePlaylist(locale_to_utf_cpy(name));
ShowMessage("Playlist \"%s\" deleted!", name.c_str());
if (!Config.local_browser && myBrowser->Main())
myBrowser->GetDirectory("/");
}
else
ShowMessage("Aborted!");
curs_set(0);
if (myPlaylistEditor->Main()) // check if initialized
myPlaylistEditor->Playlists->Clear(0); // make playlists list update itself
UnlockStatusbar();
}
else if (myScreen == myBrowser && !myBrowser->Main()->Empty() && myBrowser->Main()->Current().type != itPlaylist)
{
if (!Config.local_browser)
CHECK_MPD_MUSIC_DIR;
MPD::Item &item = myBrowser->Main()->Current();
if (item.type == itSong && !Config.allow_physical_files_deletion)
{
ShowMessage("Deleting files is disabled by default, see man page for more details");
continue;
}
if (item.type == itDirectory && !Config.allow_physical_directories_deletion)
{
ShowMessage("Deleting directories is disabled by default, see man page for more details");
continue;
}
if (item.type == itDirectory && item.song) // parent dir
continue;
LockStatusbar();
std::string name = item.type == itSong ? item.song->GetName() : item.name;
Statusbar() << "Delete " << (item.type == itSong ? "file" : "directory") << " \"" << name << "\" ? [y/n] ";
curs_set(1);
int in = 0;
do
{
TraceMpdStatus();
wFooter->ReadKey(in);
}
while (in != 'y' && in != 'n');
if (in == 'y')
{
std::string path;
if (!Config.local_browser)
path = Config.mpd_music_dir;
path += item.type == itSong ? item.song->GetFile() : item.name;
if (item.type == itDirectory)
myBrowser->ClearDirectory(path);
if (remove(path.c_str()) == 0)
{
ShowMessage("\"%s\" has been successfuly deleted!", name.c_str());
if (!Config.local_browser)
Mpd.UpdateDirectory(myBrowser->CurrentDir());
else
myBrowser->GetDirectory(myBrowser->CurrentDir());
}
else
ShowMessage("Couldn't remove \"%s\": %s", name.c_str(), strerror(errno));
}
else
ShowMessage("Aborted!");
curs_set(0);
UnlockStatusbar();
}
else if (myScreen->ActiveWindow() == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty())
{
if (myPlaylistEditor->Content->hasSelected())
{
std::vector<size_t> list;
myPlaylistEditor->Content->GetSelected(list);
std::string playlist = locale_to_utf_cpy(myPlaylistEditor->Playlists->Current());
ShowMessage("Deleting selected items...");
Mpd.StartCommandsList();
for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it)
{
Mpd.Delete(playlist, *it);
myPlaylistEditor->Content->DeleteOption(*it);
}
Mpd.CommitCommandsList();
ShowMessage("Selected items deleted from playlist \"%s\"!", myPlaylistEditor->Playlists->Current().c_str());
}
else
{
myPlaylistEditor->Content->SetTimeout(50);
locale_to_utf(myPlaylistEditor->Playlists->Current());
while (!myPlaylistEditor->Content->Empty() && Keypressed(input, Key.Delete))
{
TraceMpdStatus();
myPlaylist->UpdateTimer();
Mpd.Delete(myPlaylistEditor->Playlists->Current(), myPlaylistEditor->Content->Choice());
myPlaylistEditor->Content->DeleteOption(myPlaylistEditor->Content->Choice());
myPlaylistEditor->Content->Refresh();
myPlaylistEditor->Content->ReadKey(input);
}
utf_to_locale(myPlaylistEditor->Playlists->Current());
myPlaylistEditor->Content->SetTimeout(ncmpcpp_window_timeout);
}
}
}
else if (Keypressed(input, Key.Prev))
{
Mpd.Prev();
UpdateStatusImmediately = 1;
}
else if (Keypressed(input, Key.Next))
{
Mpd.Next();
UpdateStatusImmediately = 1;
}
else if (Keypressed(input, Key.Pause))
{
Mpd.Pause();
UpdateStatusImmediately = 1;
}
else if (Keypressed(input, Key.SavePlaylist))
{
LockStatusbar();
Statusbar() << "Save playlist as: ";
std::string playlist_name = wFooter->GetString();
std::string real_playlist_name = locale_to_utf_cpy(playlist_name);
UnlockStatusbar();
if (playlist_name.find("/") != std::string::npos)
{
ShowMessage("Playlist name cannot contain slashes!");
continue;
}
if (!playlist_name.empty())
{
if (myPlaylist->Items->isFiltered())
{
Mpd.StartCommandsList();
for (size_t i = 0; i < myPlaylist->Items->Size(); ++i)
Mpd.AddToPlaylist(real_playlist_name, (*myPlaylist->Items)[i]);
Mpd.CommitCommandsList();
if (Mpd.GetErrorMessage().empty())
ShowMessage("Filtered items added to playlist \"%s\"", playlist_name.c_str());
}
else if (Mpd.SavePlaylist(real_playlist_name))
{
ShowMessage("Playlist saved as: %s", playlist_name.c_str());
if (myPlaylistEditor->Main()) // check if initialized
myPlaylistEditor->Playlists->Clear(0); // make playlist's list update itself
}
else
{
LockStatusbar();
Statusbar() << "Playlist already exists, overwrite: " << playlist_name << " ? [y/n] ";
curs_set(1);
int in = 0;
MessagesAllowed = 0;
while (in != 'y' && in != 'n')
{
TraceMpdStatus();
wFooter->ReadKey(in);
}
MessagesAllowed = 1;
if (in == 'y')
{
Mpd.DeletePlaylist(real_playlist_name);
if (Mpd.SavePlaylist(real_playlist_name))
ShowMessage("Playlist overwritten!");
}
else
ShowMessage("Aborted!");
curs_set(0);
if (myPlaylistEditor->Main()) // check if initialized
myPlaylistEditor->Playlists->Clear(0); // make playlist's list update itself
UnlockStatusbar();
if (myScreen == myPlaylist)
myPlaylist->EnableHighlighting();
}
}
if (!Config.local_browser
&& myBrowser->Main()
&& myBrowser->CurrentDir() == "/"
&& !myBrowser->Main()->Empty())
myBrowser->GetDirectory(myBrowser->CurrentDir());
}
else if (Keypressed(input, Key.Stop))
{
Mpd.Stop();
UpdateStatusImmediately = 1;
}
else if (Keypressed(input, Key.MvSongUp))
{
if (myScreen == myPlaylist && myPlaylist->SortingInProgress())
myPlaylist->AdjustSortOrder(input);
else if (myScreen == myPlaylist && !myPlaylist->Items->Empty())
{
CHECK_PLAYLIST_FOR_FILTERING;
myPlaylist->Items->SetTimeout(50);
if (myPlaylist->Items->hasSelected())
{
std::vector<size_t> list;
myPlaylist->Items->GetSelected(list);
for (std::vector<size_t>::iterator it = list.begin(); it != list.end(); ++it)
if (*it == size_t(myPlaylist->NowPlaying) && list.front() > 0)
myPlaylist->Items->BoldOption(myPlaylist->NowPlaying, 0);
std::vector<size_t> origs(list);
while (Keypressed(input, Key.MvSongUp) && list.front() > 0)
{
TraceMpdStatus();
Playlist::BlockUpdate = 1;
myPlaylist->UpdateTimer();
for (std::vector<size_t>::iterator it = list.begin(); it != list.end(); ++it)
{
--*it;
myPlaylist->Items->at((*it)+1).SetPosition(*it);
myPlaylist->Items->at(*it).SetPosition((*it)+1);
myPlaylist->Items->Swap(*it, (*it)+1);
}
myPlaylist->Items->Highlight(list[(list.size()-1)/2]);
myPlaylist->Items->Refresh();
myPlaylist->Items->ReadKey(input);
}
Mpd.StartCommandsList();
for (size_t i = 0; i < list.size(); ++i)
Mpd.Move(origs[i], list[i]);
Mpd.CommitCommandsList();
}
else
{
size_t from, to;
from = to = myPlaylist->Items->Choice();
// unbold now playing as if song changes during move, this won't be unbolded.
if (to == size_t(myPlaylist->NowPlaying) && to > 0)
myPlaylist->Items->BoldOption(myPlaylist->NowPlaying, 0);
while (Keypressed(input, Key.MvSongUp) && to > 0)
{
TraceMpdStatus();
Playlist::BlockUpdate = 1;
myPlaylist->UpdateTimer();
--to;
myPlaylist->Items->at(from).SetPosition(to);
myPlaylist->Items->at(to).SetPosition(from);
myPlaylist->Items->Swap(to, to+1);
myPlaylist->Items->Scroll(wUp);
myPlaylist->Items->Refresh();
myPlaylist->Items->ReadKey(input);
}
Mpd.Move(from, to);
UpdateStatusImmediately = 1;
}
myPlaylist->Items->SetTimeout(ncmpcpp_window_timeout);
}
else if (myScreen->ActiveWindow() == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty())
{
myPlaylistEditor->Content->SetTimeout(50);
if (myPlaylistEditor->Content->hasSelected())
{
std::vector<size_t> list;
myPlaylistEditor->Content->GetSelected(list);
std::vector<size_t> origs(list);
while (Keypressed(input, Key.MvSongUp) && list.front() > 0)
{
TraceMpdStatus();
myPlaylist->UpdateTimer();
for (std::vector<size_t>::iterator it = list.begin(); it != list.end(); ++it)
{
--*it;
myPlaylistEditor->Content->Swap(*it, (*it)+1);
}
myPlaylistEditor->Content->Highlight(list[(list.size()-1)/2]);
myPlaylistEditor->Content->Refresh();
myPlaylistEditor->Content->ReadKey(input);
}
Mpd.StartCommandsList();
for (size_t i = 0; i < list.size(); ++i)
if (origs[i] != list[i])
Mpd.Move(myPlaylistEditor->Playlists->Current(), origs[i], list[i]);
Mpd.CommitCommandsList();
}
else
{
size_t from, to;
from = to = myPlaylistEditor->Content->Choice();
while (Keypressed(input, Key.MvSongUp) && to > 0)
{
TraceMpdStatus();
myPlaylist->UpdateTimer();
--to;
myPlaylistEditor->Content->Swap(to, to+1);
myPlaylistEditor->Content->Scroll(wUp);
myPlaylistEditor->Content->Refresh();
myPlaylistEditor->Content->ReadKey(input);
}
if (from != to)
Mpd.Move(myPlaylistEditor->Playlists->Current(), from, to);
}
myPlaylistEditor->Content->SetTimeout(ncmpcpp_window_timeout);
}
}
else if (Keypressed(input, Key.MvSongDown))
{
if (myScreen == myPlaylist && myPlaylist->SortingInProgress())
myPlaylist->AdjustSortOrder(input);
else if (myScreen == myPlaylist && !myPlaylist->Items->Empty())
{
CHECK_PLAYLIST_FOR_FILTERING;
myPlaylist->Items->SetTimeout(50);
if (myPlaylist->Items->hasSelected())
{
std::vector<size_t> list;
myPlaylist->Items->GetSelected(list);
for (std::vector<size_t>::iterator it = list.begin(); it != list.end(); ++it)
if (*it == size_t(myPlaylist->NowPlaying) && list.back() < myPlaylist->Items->Size()-1)
myPlaylist->Items->BoldOption(myPlaylist->NowPlaying, 0);
std::vector<size_t> origs(list);
while (Keypressed(input, Key.MvSongDown) && list.back() < myPlaylist->Items->Size()-1)
{
TraceMpdStatus();
Playlist::BlockUpdate = 1;
myPlaylist->UpdateTimer();
for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it)
{
++*it;
myPlaylist->Items->at((*it)-1).SetPosition(*it);
myPlaylist->Items->at(*it).SetPosition((*it)-1);
myPlaylist->Items->Swap(*it, (*it)-1);
}
myPlaylist->Items->Highlight(list[(list.size()-1)/2]);
myPlaylist->Items->Refresh();
myPlaylist->Items->ReadKey(input);
}
Mpd.StartCommandsList();
for (int i = list.size()-1; i >= 0; --i)
Mpd.Move(origs[i], list[i]);
Mpd.CommitCommandsList();
}
else
{
size_t from, to;
from = to = myPlaylist->Items->Choice();
// unbold now playing as if song changes during move, this won't be unbolded.
if (to == size_t(myPlaylist->NowPlaying) && to < myPlaylist->Items->Size()-1)
myPlaylist->Items->BoldOption(myPlaylist->NowPlaying, 0);
while (Keypressed(input, Key.MvSongDown) && to < myPlaylist->Items->Size()-1)
{
TraceMpdStatus();
Playlist::BlockUpdate = 1;
myPlaylist->UpdateTimer();
++to;
myPlaylist->Items->at(from).SetPosition(to);
myPlaylist->Items->at(to).SetPosition(from);
myPlaylist->Items->Swap(to, to-1);
myPlaylist->Items->Scroll(wDown);
myPlaylist->Items->Refresh();
myPlaylist->Items->ReadKey(input);
}
Mpd.Move(from, to);
UpdateStatusImmediately = 1;
}
myPlaylist->Items->SetTimeout(ncmpcpp_window_timeout);
}
else if (myScreen->ActiveWindow() == myPlaylistEditor->Content && !myPlaylistEditor->Content->Empty())
{
myPlaylistEditor->Content->SetTimeout(50);
if (myPlaylistEditor->Content->hasSelected())
{
std::vector<size_t> list;
myPlaylistEditor->Content->GetSelected(list);
std::vector<size_t> origs(list);
while (Keypressed(input, Key.MvSongDown) && list.back() < myPlaylistEditor->Content->Size()-1)
{
TraceMpdStatus();
myPlaylist->UpdateTimer();
for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it)
{
++*it;
myPlaylistEditor->Content->Swap(*it, (*it)-1);
}
myPlaylistEditor->Content->Highlight(list[(list.size()-1)/2]);
myPlaylistEditor->Content->Refresh();
myPlaylistEditor->Content->ReadKey(input);
}
Mpd.StartCommandsList();
for (int i = list.size()-1; i >= 0; --i)
if (origs[i] != list[i])
Mpd.Move(myPlaylistEditor->Playlists->Current(), origs[i], list[i]);
Mpd.CommitCommandsList();
}
else
{
size_t from, to;
from = to = myPlaylistEditor->Content->Choice();
while (Keypressed(input, Key.MvSongDown) && to < myPlaylistEditor->Content->Size()-1)
{
TraceMpdStatus();
myPlaylist->UpdateTimer();
++to;
myPlaylistEditor->Content->Swap(to, to-1);
myPlaylistEditor->Content->Scroll(wDown);
myPlaylistEditor->Content->Refresh();
myPlaylistEditor->Content->ReadKey(input);
}
if (from != to)
Mpd.Move(myPlaylistEditor->Playlists->Current(), from, to);
}
myPlaylistEditor->Content->SetTimeout(ncmpcpp_window_timeout);
}
}
else if (Keypressed(input, Key.MoveTo) && myScreen == myPlaylist)
{
CHECK_PLAYLIST_FOR_FILTERING;
if (!myPlaylist->Items->hasSelected())
{
ShowMessage("No selected items to move!");
continue;
}
Playlist::BlockUpdate = 1;
size_t pos = myPlaylist->Items->Choice();
// if cursor is at the last item, break convention and move at the end of playlist
if (pos == myPlaylist->Items->Size()-1)
pos++;
std::vector<size_t> list;
myPlaylist->Items->GetSelected(list);
if (pos >= list.front() && pos <= list.back())
continue;
int diff = pos-list.front();
Mpd.StartCommandsList();
if (diff > 0)
{
pos -= list.size();
size_t i = list.size()-1;
for (std::vector<size_t>::reverse_iterator it = list.rbegin(); it != list.rend(); ++it, --i)
{
Mpd.Move(*it, pos+i);
myPlaylist->Items->Move(*it, pos+i);
}
}
else if (diff < 0)
{
size_t i = 0;
for (std::vector<size_t>::const_iterator it = list.begin(); it != list.end(); ++it, ++i)
{
Mpd.Move(*it, pos+i);
myPlaylist->Items->Move(*it, pos+i);
}
}
Mpd.CommitCommandsList();
myPlaylist->Items->Highlight(pos);
myPlaylist->FixPositions();
}
else if (Keypressed(input, Key.Add))
{
if (myScreen == myPlaylistEditor && myPlaylistEditor->Playlists->Empty())
continue;
LockStatusbar();
Statusbar() << (myScreen == myPlaylistEditor ? "Add to playlist: " : "Add: ");
std::string path = wFooter->GetString();
UnlockStatusbar();
if (!path.empty())
{
if (myScreen == myPlaylistEditor)
{
Mpd.AddToPlaylist(myPlaylistEditor->Playlists->Current(), path);
myPlaylistEditor->Content->Clear(0); // make it refetch content of playlist
}
else
Mpd.Add(path);
UpdateStatusImmediately = 1;
}
}
else if (Keypressed(input, Key.SeekForward) || Keypressed(input, Key.SeekBackward))
{
if (!Mpd.GetTotalTime())
{
ShowMessage("Unknown item length!");
continue;
}
const Song *s = myPlaylist->NowPlayingSong();
if (!s)
continue;
LockProgressbar();
LockStatusbar();
int songpos;
time_t t = time(0);
songpos = Mpd.GetElapsedTime();
SeekingInProgress = 1;
while (Keypressed(input, Key.SeekForward) || Keypressed(input, Key.SeekBackward))
{
TraceMpdStatus();
myPlaylist->UpdateTimer();
myPlaylist->Items->ReadKey(input);
int howmuch = Config.incremental_seeking ? (myPlaylist->Timer()-t)/2+Config.seek_time : Config.seek_time;
if (Keypressed(input, Key.SeekForward) && songpos < Mpd.GetTotalTime())
{
songpos += howmuch;
if (songpos > Mpd.GetTotalTime())
songpos = Mpd.GetTotalTime();
}
else if (Keypressed(input, Key.SeekBackward) && songpos > 0)
{
songpos -= howmuch;
if (songpos < 0)
songpos = 0;
}
std::string tracklength;
*wFooter << fmtBold;
if (Config.new_design)
{
if (Config.display_remaining_time)
{
tracklength = "-";
tracklength += Song::ShowTime(Mpd.GetTotalTime()-songpos);
}
else
tracklength = Song::ShowTime(songpos);
tracklength += "/";
tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime());
*wHeader << XY(0, 0) << tracklength << " ";
wHeader->Refresh();
}
else
{
tracklength = "[";
if (Config.display_remaining_time)
{
tracklength += "-";
tracklength += Song::ShowTime(Mpd.GetTotalTime()-songpos);
}
else
tracklength = Song::ShowTime(songpos);
tracklength += "/";
tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime());
tracklength += "]";
*wFooter << XY(wFooter->GetWidth()-tracklength.length(), 1) << tracklength;
}
double progressbar_size = songpos/double(Mpd.GetTotalTime());
unsigned howlong = wFooter->GetWidth()*progressbar_size;
mvwhline(wFooter->Raw(), 0, 0, 0, wFooter->GetWidth());
for (unsigned i = 0; i < howlong; ++i)
*wFooter << Config.progressbar[0];
if (howlong < wFooter->GetWidth())
*wFooter << Config.progressbar[1];
*wFooter << fmtBoldEnd;
wFooter->Refresh();
}
Mpd.Seek(songpos);
SeekingInProgress = 0;
UpdateStatusImmediately = 1;
UnlockProgressbar();
UnlockStatusbar();
}
else if (Keypressed(input, Key.ToggleDisplayMode))
{
if (myScreen == myPlaylist)
{
Config.columns_in_playlist = !Config.columns_in_playlist;
ShowMessage("Playlist display mode: %s", Config.columns_in_playlist ? "Columns" : "Classic");
if (Config.columns_in_playlist)
{
myPlaylist->Items->SetItemDisplayer(Display::SongsInColumns);
myPlaylist->Items->SetTitle(Display::Columns());
myPlaylist->Items->SetGetStringFunction(Playlist::SongInColumnsToString);
}
else
{
myPlaylist->Items->SetItemDisplayer(Display::Songs);
myPlaylist->Items->SetTitle("");
myPlaylist->Items->SetGetStringFunction(Playlist::SongToString);
}
}
else if (myScreen == myBrowser)
{
Config.columns_in_browser = !Config.columns_in_browser;
ShowMessage("Browser display mode: %s", Config.columns_in_browser ? "Columns" : "Classic");
myBrowser->Main()->SetTitle(Config.columns_in_browser ? Display::Columns() : "");
}
else if (myScreen == mySearcher)
{
Config.columns_in_search_engine = !Config.columns_in_search_engine;
ShowMessage("Search engine display mode: %s", Config.columns_in_search_engine ? "Columns" : "Classic");
if (mySearcher->Main()->Size() > SearchEngine::StaticOptions)
mySearcher->Main()->SetTitle(Config.columns_in_search_engine ? Display::Columns() : "");
}
}
# ifdef HAVE_CURL_CURL_H
else if (Keypressed(input, Key.ToggleLyricsDB))
{
const char *current_lyrics_plugin = Lyrics::GetPluginName(++Config.lyrics_db);
if (!current_lyrics_plugin)
{
current_lyrics_plugin = Lyrics::GetPluginName(Config.lyrics_db = 0);
}
ShowMessage("Using lyrics database: %s", current_lyrics_plugin);
}
# endif // HAVE_CURL_CURL_H
else if (Keypressed(input, Key.ToggleAutoCenter))
{
Config.autocenter_mode = !Config.autocenter_mode;
ShowMessage("Auto center mode: %s", Config.autocenter_mode ? "On" : "Off");
if (Config.autocenter_mode && myPlaylist->isPlaying() && !myPlaylist->Items->isFiltered())
myPlaylist->Items->Highlight(myPlaylist->NowPlaying);
}
else if (Keypressed(input, Key.UpdateDB))
{
if (myScreen == myBrowser)
Mpd.UpdateDirectory(locale_to_utf_cpy(myBrowser->CurrentDir()));
# ifdef HAVE_TAGLIB_H
else if (myScreen == myTagEditor && !Config.albums_in_tag_editor)
Mpd.UpdateDirectory(myTagEditor->CurrentDir());
# endif // HAVE_TAGLIB_H
else
Mpd.UpdateDirectory("/");
}
else if (Keypressed(input, Key.GoToNowPlaying))
{
if (myScreen == myPlaylist && myPlaylist->isPlaying())
{
CHECK_PLAYLIST_FOR_FILTERING;
myPlaylist->Items->Highlight(myPlaylist->NowPlaying);
}
else if (myScreen == myBrowser)
{
if (const Song *s = myPlaylist->NowPlayingSong())
{
myBrowser->LocateSong(*s);
RedrawHeader = 1;
}
}
}
else if (Keypressed(input, Key.ToggleRepeat))
{
Mpd.SetRepeat(!Mpd.GetRepeat());
UpdateStatusImmediately = 1;
}
else if (Keypressed(input, Key.Shuffle))
{
Mpd.Shuffle();
UpdateStatusImmediately = 1;
}
else if (Keypressed(input, Key.ToggleRandom))
{
Mpd.SetRandom(!Mpd.GetRandom());
UpdateStatusImmediately = 1;
}
else if (Keypressed(input, Key.ToggleSingle))
{
if (myScreen == mySearcher && !mySearcher->Main()->isStatic(0))
{
mySearcher->Main()->Highlight(SearchEngine::SearchButton);
mySearcher->Main()->Highlighting(0);
mySearcher->Main()->Refresh();
mySearcher->Main()->Highlighting(1);
mySearcher->EnterPressed();
}
# ifdef HAVE_TAGLIB_H
else if (myScreen == myTinyTagEditor)
{
myTinyTagEditor->Main()->Highlight(myTinyTagEditor->Main()->Size()-2); // Save
myTinyTagEditor->EnterPressed();
}
# endif // HAVE_TAGLIB_H
else
{
Mpd.SetSingle(!Mpd.GetSingle());
UpdateStatusImmediately = 1;
}
}
else if (Keypressed(input, Key.ToggleConsume))
{
Mpd.SetConsume(!Mpd.GetConsume());
UpdateStatusImmediately = 1;
}
else if (Keypressed(input, Key.ToggleCrossfade))
{
Mpd.SetCrossfade(Mpd.GetCrossfade() ? 0 : Config.crossfade_time);
UpdateStatusImmediately = 1;
}
else if (Keypressed(input, Key.SetCrossfade))
{
LockStatusbar();
Statusbar() << "Set crossfade to: ";
std::string crossfade = wFooter->GetString(3);
UnlockStatusbar();
int cf = StrToInt(crossfade);
if (cf > 0)
{
Config.crossfade_time = cf;
Mpd.SetCrossfade(cf);
UpdateStatusImmediately = 1;
}
}
else if (Keypressed(input, Key.EditTags))
{
if (myScreen != myBrowser || !Config.local_browser)
CHECK_MPD_MUSIC_DIR;
# ifdef HAVE_TAGLIB_H
if (myTinyTagEditor->SetEdited(myScreen->CurrentSong()))
{
myTinyTagEditor->SwitchTo();
}
else if (myScreen->ActiveWindow() == myLibrary->Artists)
{
LockStatusbar();
Statusbar() << fmtBold << IntoStr(Config.media_lib_primary_tag) << fmtBoldEnd << ": ";
std::string new_tag = wFooter->GetString(myLibrary->Artists->Current());
UnlockStatusbar();
if (!new_tag.empty() && new_tag != myLibrary->Artists->Current())
{
bool success = 1;
SongList list;
ShowMessage("Updating tags...");
Mpd.StartSearch(1);
Mpd.AddSearch(Config.media_lib_primary_tag, locale_to_utf_cpy(myLibrary->Artists->Current()));
Mpd.CommitSearch(list);
Song::SetFunction set = IntoSetFunction(Config.media_lib_primary_tag);
if (!set)
continue;
for (SongList::iterator it = list.begin(); it != list.end(); ++it)
{
(*it)->Localize();
((*it)->*set)(new_tag);
ShowMessage("Updating tags in \"%s\"...", (*it)->GetName().c_str());
std::string path = Config.mpd_music_dir + (*it)->GetFile();
if (!TagEditor::WriteTags(**it))
{
ShowMessage("Error updating tags in \"%s\"!", (*it)->GetFile().c_str());
success = 0;
break;
}
}
if (success)
{
Mpd.UpdateDirectory(locale_to_utf_cpy(FindSharedDir(list)));
ShowMessage("Tags updated succesfully!");
}
FreeSongList(list);
}
}
else if (myScreen->ActiveWindow() == myLibrary->Albums)
{
LockStatusbar();
Statusbar() << fmtBold << "Album: " << fmtBoldEnd;
std::string new_album = wFooter->GetString(myLibrary->Albums->Current().second.Album);
UnlockStatusbar();
if (!new_album.empty() && new_album != myLibrary->Albums->Current().second.Album)
{
bool success = 1;
ShowMessage("Updating tags...");
for (size_t i = 0; i < myLibrary->Songs->Size(); ++i)
{
(*myLibrary->Songs)[i].Localize();
ShowMessage("Updating tags in \"%s\"...", (*myLibrary->Songs)[i].GetName().c_str());
std::string path = Config.mpd_music_dir + (*myLibrary->Songs)[i].GetFile();
TagLib::FileRef f(locale_to_utf_cpy(path).c_str());
if (f.isNull())
{
ShowMessage("Error opening file \"%s\"!", (*myLibrary->Songs)[i].GetFile().c_str());
success = 0;
break;
}
f.tag()->setAlbum(ToWString(new_album));
if (!f.save())
{
ShowMessage("Error writing tags in \"%s\"!", (*myLibrary->Songs)[i].GetFile().c_str());
success = 0;
break;
}
}
if (success)
{
Mpd.UpdateDirectory(locale_to_utf_cpy(FindSharedDir(myLibrary->Songs)));
ShowMessage("Tags updated succesfully!");
}
}
}
else if (myScreen->ActiveWindow() == myTagEditor->Dirs)
{
std::string old_dir = myTagEditor->Dirs->Current().first;
LockStatusbar();
Statusbar() << fmtBold << "Directory: " << fmtBoldEnd;
std::string new_dir = wFooter->GetString(old_dir);
UnlockStatusbar();
if (!new_dir.empty() && new_dir != old_dir)
{
std::string full_old_dir = Config.mpd_music_dir + myTagEditor->CurrentDir() + "/" + locale_to_utf_cpy(old_dir);
std::string full_new_dir = Config.mpd_music_dir + myTagEditor->CurrentDir() + "/" + locale_to_utf_cpy(new_dir);
if (rename(full_old_dir.c_str(), full_new_dir.c_str()) == 0)
{
Mpd.UpdateDirectory(myTagEditor->CurrentDir());
}
else
{
ShowMessage("Cannot rename \"%s\" to \"%s\"!", old_dir.c_str(), new_dir.c_str());
}
}
}
else
# endif // HAVE_TAGLIB_H
if (myScreen == myLyrics)
{
myLyrics->Edit();
}
if (myScreen == myBrowser && myBrowser->Main()->Current().type == itDirectory)
{
std::string old_dir = myBrowser->Main()->Current().name;
LockStatusbar();
Statusbar() << fmtBold << "Directory: " << fmtBoldEnd;
std::string new_dir = wFooter->GetString(old_dir);
UnlockStatusbar();
if (!new_dir.empty() && new_dir != old_dir)
{
std::string full_old_dir;
if (!Config.local_browser)
full_old_dir += Config.mpd_music_dir;
full_old_dir += locale_to_utf_cpy(old_dir);
std::string full_new_dir;
if (!Config.local_browser)
full_new_dir += Config.mpd_music_dir;
full_new_dir += locale_to_utf_cpy(new_dir);
int rename_result = rename(full_old_dir.c_str(), full_new_dir.c_str());
if (rename_result == 0)
{
ShowMessage("\"%s\" renamed to \"%s\"", old_dir.c_str(), new_dir.c_str());
if (!Config.local_browser)
Mpd.UpdateDirectory(locale_to_utf_cpy(FindSharedDir(old_dir, new_dir)));
myBrowser->GetDirectory(myBrowser->CurrentDir());
}
else
ShowMessage("Cannot rename \"%s\" to \"%s\"!", old_dir.c_str(), new_dir.c_str());
}
}
else if (myScreen->ActiveWindow() == myPlaylistEditor->Playlists || (myScreen == myBrowser && myBrowser->Main()->Current().type == itPlaylist))
{
std::string old_name = myScreen->ActiveWindow() == myPlaylistEditor->Playlists ? myPlaylistEditor->Playlists->Current() : myBrowser->Main()->Current().name;
LockStatusbar();
Statusbar() << fmtBold << "Playlist: " << fmtBoldEnd;
std::string new_name = wFooter->GetString(old_name);
UnlockStatusbar();
if (!new_name.empty() && new_name != old_name)
{
Mpd.Rename(locale_to_utf_cpy(old_name), locale_to_utf_cpy(new_name));
ShowMessage("Playlist \"%s\" renamed to \"%s\"", old_name.c_str(), new_name.c_str());
if (!Config.local_browser && myBrowser->Main())
myBrowser->GetDirectory("/");
if (myPlaylistEditor->Main())
myPlaylistEditor->Playlists->Clear(0);
}
}
}
else if (Keypressed(input, Key.GoToContainingDir))
{
Song *s = myScreen->CurrentSong();
if (s)
myBrowser->LocateSong(*s);
}
else if (Keypressed(input, Key.GoToPosition))
{
if (!Mpd.GetTotalTime())
{
ShowMessage("Unknown item length!");
continue;
}
const Song *s = myPlaylist->NowPlayingSong();
if (!s)
continue;
LockStatusbar();
Statusbar() << "Position to go (in %/mm:ss/seconds(s)): ";
std::string position = wFooter->GetString();
UnlockStatusbar();
if (position.empty())
continue;
int newpos = 0;
if (position.find(':') != std::string::npos) // probably time in mm:ss
{
try
{
newpos = StrToInt(position)*60 + StrToInt(position.substr(position.find(':')+1));
}
catch (std::out_of_range) { }
if (newpos >= 0 && newpos <= Mpd.GetTotalTime())
Mpd.Seek(newpos);
else
ShowMessage("Out of bounds, 0:00-%s possible for mm:ss, %s given.", s->GetLength().c_str(), MPD::Song::ShowTime(newpos).c_str());
}
else if (position.find('s') != std::string::npos) // probably position in seconds
{
newpos = StrToInt(position);
if (newpos >= 0 && newpos <= Mpd.GetTotalTime())
Mpd.Seek(newpos);
else
ShowMessage("Out of bounds, 0-%d possible for seconds, %d given.", s->GetTotalLength(), newpos);
}
else
{
newpos = StrToInt(position);
if (newpos >= 0 && newpos <= 100)
Mpd.Seek(Mpd.GetTotalTime()*newpos/100.0);
else
ShowMessage("Out of bounds, 0-100 possible for %%, %d given.", newpos);
}
UpdateStatusImmediately = 1;
}
else if (Keypressed(input, Key.ReverseSelection))
{
if (myScreen->allowsSelection())
{
myScreen->ReverseSelection();
ShowMessage("Selection reversed!");
}
}
else if (Keypressed(input, Key.DeselectAll))
{
if (myScreen->allowsSelection())
{
if (myScreen->GetList()->Deselect())
{
ShowMessage("Items deselected!");
}
}
}
else if (Keypressed(input, Key.AddSelected))
{
mySelectedItemsAdder->SwitchTo();
}
else if (Keypressed(input, Key.Crop))
{
CHECK_PLAYLIST_FOR_FILTERING;
if (myPlaylist->Items->hasSelected())
{
Mpd.StartCommandsList();
for (int i = myPlaylist->Items->Size()-1; i >= 0; --i)
{
if (!myPlaylist->Items->isSelected(i) && i != myPlaylist->NowPlaying)
Mpd.Delete(i);
}
// if mpd deletes now playing song deletion will be sluggishly slow
// then so we have to assure it will be deleted at the very end.
if (myPlaylist->isPlaying() && !myPlaylist->Items->isSelected(myPlaylist->NowPlaying))
Mpd.DeleteID(myPlaylist->NowPlayingSong()->GetID());
ShowMessage("Deleting all items but selected...");
Mpd.CommitCommandsList();
ShowMessage("Items deleted!");
}
else
{
if (!myPlaylist->isPlaying())
{
ShowMessage("Nothing is playing now!");
continue;
}
Mpd.StartCommandsList();
for (int i = myPlaylist->Items->Size()-1; i >= 0; --i)
if (i != myPlaylist->NowPlaying)
Mpd.Delete(i);
ShowMessage("Deleting all items except now playing one...");
Mpd.CommitCommandsList();
ShowMessage("Items deleted!");
}
}
else if (Keypressed(input, Key.Clear))
{
if (myScreen == myPlaylistEditor && myPlaylistEditor->Playlists->Empty())
continue;
if (myScreen->ActiveWindow() == myPlaylistEditor->Content
|| Config.ask_before_clearing_main_playlist)
{
int in = 0;
LockStatusbar();
Statusbar() << "Do you really want to clear playlist";
if (myScreen->ActiveWindow() == myPlaylistEditor->Content)
*wFooter << " \"" << myPlaylistEditor->Playlists->Current() << "\"";
*wFooter << " ? [y/n] ";
curs_set(1);
do
{
TraceMpdStatus();
wFooter->ReadKey(in);
}
while (in != 'y' && in != 'n');
curs_set(0);
UnlockStatusbar();
if (in != 'y')
{
ShowMessage("Aborted!");
continue;
}
}
if (myPlaylist->Items->isFiltered())
{
ShowMessage("Deleting filtered items...");
Mpd.StartCommandsList();
for (int i = myPlaylist->Items->Size()-1; i >= 0; --i)
Mpd.Delete((*myPlaylist->Items)[i].GetPosition());
Mpd.CommitCommandsList();
ShowMessage("Filtered items deleted!");
}
else
{
if (myScreen->ActiveWindow() == myPlaylistEditor->Content)
{
Mpd.ClearPlaylist(locale_to_utf_cpy(myPlaylistEditor->Playlists->Current()));
myPlaylistEditor->Content->Clear(0);
}
else
{
ShowMessage("Clearing playlist...");
Mpd.ClearPlaylist();
}
}
// if playlist is cleared, items list have to be updated, but this
// can be blocked if new song was added to playlist less than one
// second ago, so we need to assume it's unlocked.
BlockItemListUpdate = 0;
UpdateStatusImmediately = 1;
}
else if (Keypressed(input, Key.SortPlaylist) && myScreen == myPlaylist)
{
CHECK_PLAYLIST_FOR_FILTERING;
myPlaylist->Sort();
}
else if (Keypressed(input, Key.ApplyFilter))
{
List *mList = myScreen->GetList();
if (!mList)
continue;
LockStatusbar();
Statusbar() << fmtBold << "Apply filter: " << fmtBoldEnd;
wFooter->SetGetStringHelper(StatusbarApplyFilterImmediately);
wFooter->GetString(mList->GetFilter());
wFooter->SetGetStringHelper(StatusbarGetStringHelper);
UnlockStatusbar();
if (mList->isFiltered())
ShowMessage("Using filter \"%s\"", mList->GetFilter().c_str());
else
ShowMessage("Filtering disabled");
if (myScreen == myPlaylist)
{
myPlaylist->EnableHighlighting();
Playlist::ReloadTotalLength = 1;
RedrawHeader = 1;
}
}
else if (Keypressed(input, Key.FindForward) || Keypressed(input, Key.FindBackward))
{
List *mList = myScreen->GetList();
if (mList)
{
LockStatusbar();
Statusbar() << "Find " << (Keypressed(input, Key.FindForward) ? "forward" : "backward") << ": ";
std::string findme = wFooter->GetString();
UnlockStatusbar();
myPlaylist->UpdateTimer();
if (!findme.empty())
ShowMessage("Searching...");
bool success = mList->Search(findme, myScreen == mySearcher ? SearchEngine::StaticOptions : 0, REG_ICASE | Config.regex_type);
if (findme.empty())
continue;
success ? ShowMessage("Searching finished!") : ShowMessage("Unable to find \"%s\"", findme.c_str());
if (Keypressed(input, Key.FindForward))
mList->NextFound(Config.wrapped_search);
else
mList->PrevFound(Config.wrapped_search);
if (myScreen == myPlaylist)
myPlaylist->EnableHighlighting();
}
else if (myScreen == myHelp || myScreen == myLyrics || myScreen == myInfo)
{
LockStatusbar();
Statusbar() << "Find: ";
std::string findme = wFooter->GetString();
UnlockStatusbar();
ShowMessage("Searching...");
Screen<Scrollpad> *s = static_cast<Screen<Scrollpad> *>(myScreen);
s->Main()->RemoveFormatting();
ShowMessage("%s", s->Main()->SetFormatting(fmtReverse, findme, fmtReverseEnd) || findme.empty() ? "Done!" : "No matching patterns found");
s->Main()->Flush();
}
}
else if (Keypressed(input, Key.NextFoundPosition) || Keypressed(input, Key.PrevFoundPosition))
{
List *mList = myScreen->GetList();
if (!mList)
continue;
if (Keypressed(input, Key.NextFoundPosition))
mList->NextFound(Config.wrapped_search);
else
mList->PrevFound(Config.wrapped_search);
}
else if (Keypressed(input, Key.ToggleFindMode))
{
Config.wrapped_search = !Config.wrapped_search;
ShowMessage("Search mode: %s", Config.wrapped_search ? "Wrapped" : "Normal");
}
else if (Keypressed(input, Key.ToggleSpaceMode))
{
Config.space_selects = !Config.space_selects;
ShowMessage("Space mode: %s item", Config.space_selects ? "Select/deselect" : "Add");
}
else if (Keypressed(input, Key.ToggleAddMode))
{
Config.ncmpc_like_songs_adding = !Config.ncmpc_like_songs_adding;
ShowMessage("Add mode: %s", Config.ncmpc_like_songs_adding ? "Add item to playlist, remove if already added" : "Always add item to playlist");
}
else if (Keypressed(input, Key.ToggleMouse))
{
Config.mouse_support = !Config.mouse_support;
mousemask(Config.mouse_support ? ALL_MOUSE_EVENTS : 0, 0);
ShowMessage("Mouse support %s", Config.mouse_support ? "enabled" : "disabled");
}
else if (Keypressed(input, Key.SwitchTagTypeList))
{
if (myScreen == myPlaylist)
{
LockStatusbar();
Statusbar() << "Number of random songs: ";
size_t number = StrToLong(wFooter->GetString());
UnlockStatusbar();
if (number && Mpd.AddRandomSongs(number))
ShowMessage("%zu random song%s added to playlist!", number, number == 1 ? "" : "s");
}
else if (myScreen == myBrowser)
{
myBrowser->ChangeBrowseMode();
}
else if (myScreen->ActiveWindow() == myLibrary->Artists
|| (myLibrary->Columns() == 2 && myScreen->ActiveWindow() == myLibrary->Albums))
{
LockStatusbar();
Statusbar() << "Tag type ? [" << fmtBold << 'a' << fmtBoldEnd << "rtist/" << fmtBold << 'y' << fmtBoldEnd << "ear/" << fmtBold << 'g' << fmtBoldEnd << "enre/" << fmtBold << 'c' << fmtBoldEnd << "omposer/" << fmtBold << 'p' << fmtBoldEnd << "erformer] ";
int item;
curs_set(1);
do
{
TraceMpdStatus();
wFooter->ReadKey(item);
}
while (item != 'a' && item != 'y' && item != 'g' && item != 'c' && item != 'p');
curs_set(0);
UnlockStatusbar();
mpd_TagItems new_tagitem = IntoTagItem(item);
if (new_tagitem != Config.media_lib_primary_tag)
{
Config.media_lib_primary_tag = new_tagitem;
std::string item_type = IntoStr(Config.media_lib_primary_tag);
myLibrary->Artists->SetTitle(item_type + "s");
myLibrary->Artists->Reset();
ToLower(item_type);
if (myLibrary->Columns() == 2)
{
myLibrary->Songs->Clear(0);
myLibrary->Albums->Reset();
myLibrary->Albums->Clear();
myLibrary->Albums->SetTitle("Albums (sorted by " + item_type + ")");
myLibrary->Albums->Display();
}
else
{
myLibrary->Artists->Clear(0);
myLibrary->Artists->Display();
}
ShowMessage("Switched to list of %s tag", item_type.c_str());
}
}
}
else if (Keypressed(input, Key.SongInfo))
{
myInfo->GetSong();
}
# ifdef HAVE_CURL_CURL_H
else if (Keypressed(input, Key.ArtistInfo))
{
myInfo->GetArtist();
}
# endif // HAVE_CURL_CURL_H
else if (Keypressed(input, Key.Lyrics))
{
myLyrics->SwitchTo();
}
else if (Keypressed(input, Key.Quit))
{
main_exit = 1;
}
# ifdef HAVE_TAGLIB_H
else if (myScreen == myTinyTagEditor)
{
continue;
}
# endif // HAVE_TAGLIB_H
else if (Keypressed(input, Key.Help))
{
myHelp->SwitchTo();
}
else if (Keypressed(input, Key.ScreenSwitcher))
{
if (myScreen == myPlaylist)
myBrowser->SwitchTo();
else
myPlaylist->SwitchTo();
}
else if (Keypressed(input, Key.Playlist))
{
myPlaylist->SwitchTo();
}
else if (Keypressed(input, Key.Browser))
{
myBrowser->SwitchTo();
}
else if (Keypressed(input, Key.SearchEngine))
{
mySearcher->SwitchTo();
}
else if (Keypressed(input, Key.MediaLibrary))
{
myLibrary->SwitchTo();
}
else if (Keypressed(input, Key.PlaylistEditor))
{
myPlaylistEditor->SwitchTo();
}
# ifdef HAVE_TAGLIB_H
else if (Keypressed(input, Key.TagEditor))
{
CHECK_MPD_MUSIC_DIR;
myTagEditor->SwitchTo();
}
# endif // HAVE_TAGLIB_H
# ifdef ENABLE_OUTPUTS
else if (Keypressed(input, Key.Outputs))
{
myOutputs->SwitchTo();
}
# endif // ENABLE_OUTPUTS
# ifdef ENABLE_VISUALIZER
else if (Keypressed(input, Key.Visualizer))
{
myVisualizer->SwitchTo();
}
# endif // ENABLE_VISUALIZER
# ifdef ENABLE_CLOCK
else if (Keypressed(input, Key.Clock))
{
myClock->SwitchTo();
}
# endif // ENABLE_CLOCK
// key mapping end
}
return 0;
}