separate some code from ncmpcpp.cpp
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
bin_PROGRAMS = ncmpcpp
|
||||
ncmpcpp_SOURCES = browser.cpp charset.cpp clock.cpp display.cpp help.cpp \
|
||||
helpers.cpp libmpdclient.c lyrics.cpp misc.cpp mpdpp.cpp ncmpcpp.cpp scrollpad.cpp \
|
||||
search_engine.cpp settings.cpp song.cpp status_checker.cpp str_pool.c tag_editor.cpp \
|
||||
window.cpp
|
||||
helpers.cpp info.cpp libmpdclient.c lyrics.cpp media_library.cpp misc.cpp mpdpp.cpp \
|
||||
ncmpcpp.cpp playlist.cpp playlist_editor.cpp scrollpad.cpp search_engine.cpp \
|
||||
settings.cpp song.cpp status_checker.cpp str_pool.c tag_editor.cpp window.cpp
|
||||
|
||||
# set the include path found by configure
|
||||
INCLUDES= $(all_includes)
|
||||
@@ -10,5 +10,6 @@ INCLUDES= $(all_includes)
|
||||
# the library search path.
|
||||
ncmpcpp_LDFLAGS = $(all_libraries)
|
||||
noinst_HEADERS = browser.h charset.h clock.h display.h global.h help.h \
|
||||
helpers.h lyrics.h menu.h mpdpp.h scrollpad.h search_engine.h settings.h song.h \
|
||||
status_checker.h tag_editor.h window.h
|
||||
helpers.h info.h lyrics.h media_library.h menu.h mpdpp.h playlist_editor.h \
|
||||
scrollpad.h search_engine.h settings.h song.h status_checker.h tag_editor.h \
|
||||
window.h
|
||||
|
||||
185
src/browser.cpp
185
src/browser.cpp
@@ -28,6 +28,7 @@
|
||||
#include "global.h"
|
||||
#include "helpers.h"
|
||||
#include "settings.h"
|
||||
#include "status_checker.h"
|
||||
#ifdef HAVE_TAGLIB_H
|
||||
# include "tag_editor.h"
|
||||
#endif // HAVE_TAGLIB_H
|
||||
@@ -36,6 +37,190 @@ using namespace Global;
|
||||
using namespace MPD;
|
||||
using std::string;
|
||||
|
||||
Menu<Item> *Global::mBrowser;
|
||||
|
||||
void Browser::Init()
|
||||
{
|
||||
mBrowser = new Menu<Item>(0, main_start_y, COLS, main_height, "", Config.main_color, brNone);
|
||||
mBrowser->HighlightColor(Config.main_highlight_color);
|
||||
mBrowser->SetTimeout(ncmpcpp_window_timeout);
|
||||
mBrowser->SetSelectPrefix(&Config.selected_item_prefix);
|
||||
mBrowser->SetSelectSuffix(&Config.selected_item_suffix);
|
||||
mBrowser->SetItemDisplayer(Display::Items);
|
||||
}
|
||||
|
||||
void Browser::Resize()
|
||||
{
|
||||
mBrowser->Resize(COLS, main_height);
|
||||
}
|
||||
|
||||
void Browser::SwitchTo()
|
||||
{
|
||||
if (current_screen != csBrowser
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
&& current_screen != csTinyTagEditor
|
||||
# endif // HAVE_TAGLIB_H
|
||||
)
|
||||
{
|
||||
CLEAR_FIND_HISTORY;
|
||||
mBrowser->Empty() ? GetDirectory(browsed_dir) : UpdateItemList(mBrowser);
|
||||
wCurrent = mBrowser;
|
||||
wCurrent->Hide();
|
||||
current_screen = csBrowser;
|
||||
// redraw_screen = 1;
|
||||
redraw_header = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Browser::EnterPressed()
|
||||
{
|
||||
if (mBrowser->Empty())
|
||||
return;
|
||||
|
||||
const Item &item = mBrowser->Current();
|
||||
switch (item.type)
|
||||
{
|
||||
case itDirectory:
|
||||
{
|
||||
CLEAR_FIND_HISTORY;
|
||||
GetDirectory(item.name, browsed_dir);
|
||||
redraw_header = 1;
|
||||
break;
|
||||
}
|
||||
case itSong:
|
||||
{
|
||||
block_item_list_update = 1;
|
||||
if (Config.ncmpc_like_songs_adding && mBrowser->isBold())
|
||||
{
|
||||
bool found = 0;
|
||||
long long hash = mBrowser->Current().song->GetHash();
|
||||
for (size_t i = 0; i < mPlaylist->Size(); i++)
|
||||
{
|
||||
if (mPlaylist->at(i).GetHash() == hash)
|
||||
{
|
||||
Mpd->Play(i);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
Song &s = *item.song;
|
||||
int id = Mpd->AddSong(s);
|
||||
if (id >= 0)
|
||||
{
|
||||
Mpd->PlayID(id);
|
||||
ShowMessage("Added to playlist: %s", s.toString(Config.song_status_format).c_str());
|
||||
mBrowser->BoldOption(mBrowser->Choice(), 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case itPlaylist:
|
||||
{
|
||||
SongList list;
|
||||
Mpd->GetPlaylistContent(locale_to_utf_cpy(item.name), list);
|
||||
for (SongList::const_iterator it = list.begin(); it != list.end(); it++)
|
||||
Mpd->QueueAddSong(**it);
|
||||
if (Mpd->CommitQueue())
|
||||
{
|
||||
ShowMessage("Loading and playing playlist %s...", item.name.c_str());
|
||||
Song *s = &mPlaylist->at(mPlaylist->Size()-list.size());
|
||||
if (s->GetHash() == list[0]->GetHash())
|
||||
Mpd->PlayID(s->GetID());
|
||||
else
|
||||
ShowMessage("%s", message_part_of_songs_added);
|
||||
}
|
||||
FreeSongList(list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Browser::SpacePressed()
|
||||
{
|
||||
if (mBrowser->Empty())
|
||||
return;
|
||||
|
||||
const Item &item = mBrowser->Current();
|
||||
switch (item.type)
|
||||
{
|
||||
case itDirectory:
|
||||
{
|
||||
if (browsed_dir != "/" && !mBrowser->Choice())
|
||||
break; // do not let add parent dir.
|
||||
|
||||
if (Config.local_browser)
|
||||
{
|
||||
ShowMessage("Adding whole directories from local browser is not supported!");
|
||||
break;
|
||||
}
|
||||
|
||||
SongList list;
|
||||
Mpd->GetDirectoryRecursive(locale_to_utf_cpy(item.name), list);
|
||||
|
||||
for (SongList::const_iterator it = list.begin(); it != list.end(); it++)
|
||||
Mpd->QueueAddSong(**it);
|
||||
if (Mpd->CommitQueue())
|
||||
{
|
||||
ShowMessage("Added folder: %s", item.name.c_str());
|
||||
Song &s = mPlaylist->at(mPlaylist->Size()-list.size());
|
||||
if (s.GetHash() != list[0]->GetHash())
|
||||
ShowMessage("%s", message_part_of_songs_added);
|
||||
}
|
||||
FreeSongList(list);
|
||||
break;
|
||||
}
|
||||
case itSong:
|
||||
{
|
||||
block_item_list_update = 1;
|
||||
if (Config.ncmpc_like_songs_adding && mBrowser->isBold())
|
||||
{
|
||||
block_playlist_update = 1;
|
||||
long long hash = mBrowser->Current().song->GetHash();
|
||||
for (size_t i = 0; i < mPlaylist->Size(); i++)
|
||||
{
|
||||
if (mPlaylist->at(i).GetHash() == hash)
|
||||
{
|
||||
Mpd->QueueDeleteSong(i);
|
||||
mPlaylist->DeleteOption(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
Mpd->CommitQueue();
|
||||
mBrowser->BoldOption(mBrowser->Choice(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Song &s = *item.song;
|
||||
if (Mpd->AddSong(s) != -1)
|
||||
{
|
||||
ShowMessage("Added to playlist: %s", s.toString(Config.song_status_format).c_str());
|
||||
mBrowser->BoldOption(mBrowser->Choice(), 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case itPlaylist:
|
||||
{
|
||||
SongList list;
|
||||
Mpd->GetPlaylistContent(locale_to_utf_cpy(item.name), list);
|
||||
for (SongList::const_iterator it = list.begin(); it != list.end(); it++)
|
||||
Mpd->QueueAddSong(**it);
|
||||
if (Mpd->CommitQueue())
|
||||
{
|
||||
ShowMessage("Loading playlist %s...", item.name.c_str());
|
||||
Song &s = mPlaylist->at(mPlaylist->Size()-list.size());
|
||||
if (s.GetHash() != list[0]->GetHash())
|
||||
ShowMessage("%s", message_part_of_songs_added);
|
||||
}
|
||||
FreeSongList(list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mBrowser->Scroll(wDown);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
const char *supported_extensions[] = { "wma", "asf", "rm", "mp1", "mp2", "mp3", "mp4", "m4a", "flac", "ogg", "wav", "au", "aiff", "aif", "ac3", "aac", "mpc", "it", "mod", "s3m", "xm", "wv", 0 };
|
||||
|
||||
@@ -24,6 +24,16 @@
|
||||
#include "mpdpp.h"
|
||||
#include "ncmpcpp.h"
|
||||
|
||||
namespace Browser
|
||||
{
|
||||
void Init();
|
||||
void Resize();
|
||||
void SwitchTo();
|
||||
|
||||
void EnterPressed();
|
||||
void SpacePressed();
|
||||
}
|
||||
|
||||
void UpdateItemList(Menu<MPD::Item> *);
|
||||
|
||||
void GetDirectory(std::string, std::string = "/");
|
||||
|
||||
@@ -21,12 +21,17 @@
|
||||
/// NOTICE: Major part of this code is ported from ncmpc's clock screen
|
||||
|
||||
#include "clock.h"
|
||||
#include "display.h"
|
||||
#include "global.h"
|
||||
|
||||
#ifdef ENABLE_CLOCK
|
||||
|
||||
#include "display.h"
|
||||
#include "global.h"
|
||||
#include "settings.h"
|
||||
#include "status_checker.h"
|
||||
|
||||
using namespace Global;
|
||||
|
||||
Scrollpad *Global::wClock;
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -52,14 +57,47 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
void InitClock()
|
||||
namespace Clock
|
||||
{
|
||||
const size_t width = Config.clock_display_seconds ? 60 : 40;
|
||||
const size_t height = 8;
|
||||
}
|
||||
|
||||
void Clock::Init()
|
||||
{
|
||||
wClock = new Scrollpad((COLS-width)/2, (LINES-height)/2, width, height-1, "", Config.main_color, Border(Config.main_color));
|
||||
wClock->SetTimeout(ncmpcpp_window_timeout);
|
||||
}
|
||||
|
||||
void Clock::Resize()
|
||||
{
|
||||
if (width <= size_t(COLS) && height <= main_height)
|
||||
{
|
||||
wClock->MoveTo((COLS-width)/2, (LINES-height)/2);
|
||||
if (current_screen == csClock)
|
||||
{
|
||||
mPlaylist->Hide();
|
||||
Prepare();
|
||||
wClock->Display();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Clock::Prepare()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
older[i] = newer[i] = next[i] = 0;
|
||||
}
|
||||
|
||||
void Display::Clock(Window &w, const tm *time)
|
||||
void Clock::Update()
|
||||
{
|
||||
if (width > size_t(COLS) || height > main_height)
|
||||
return;
|
||||
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
tm *time = localtime(&rawtime);
|
||||
|
||||
mask = 0;
|
||||
set(time->tm_sec % 10, 0);
|
||||
set(time->tm_sec / 10, 4);
|
||||
@@ -72,9 +110,9 @@ void Display::Clock(Window &w, const tm *time)
|
||||
|
||||
char buf[54];
|
||||
strftime(buf, 64, "%x", time);
|
||||
attron(COLOR_PAIR(Global::Config.main_color));
|
||||
mvprintw(w.GetStartY()+w.GetHeight(), w.GetStartX()+(w.GetWidth()-strlen(buf))/2, "%s", buf);
|
||||
attroff(COLOR_PAIR(Global::Config.main_color));
|
||||
attron(COLOR_PAIR(Config.main_color));
|
||||
mvprintw(wCurrent->GetStartY()+wCurrent->GetHeight(), wCurrent->GetStartX()+(wCurrent->GetWidth()-strlen(buf))/2, "%s", buf);
|
||||
attroff(COLOR_PAIR(Config.main_color));
|
||||
refresh();
|
||||
|
||||
for (int k = 0; k < 6; k++)
|
||||
@@ -83,7 +121,7 @@ void Display::Clock(Window &w, const tm *time)
|
||||
next[k] = 0;
|
||||
for (int s = 1; s >= 0; s--)
|
||||
{
|
||||
w.Reverse(s);
|
||||
wCurrent->Reverse(s);
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
long a = (newer[i] ^ older[i]) & (s ? newer : older)[i];
|
||||
@@ -96,10 +134,10 @@ void Display::Clock(Window &w, const tm *time)
|
||||
{
|
||||
if (!(a & (t << 1)))
|
||||
{
|
||||
w.GotoXY(2*j+2, i);
|
||||
wCurrent->GotoXY(2*j+2, i);
|
||||
}
|
||||
if (Global::Config.clock_display_seconds || j < 18)
|
||||
w << " ";
|
||||
*wCurrent << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,11 +146,30 @@ void Display::Clock(Window &w, const tm *time)
|
||||
older[i] = newer[i];
|
||||
}
|
||||
}
|
||||
if (!s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Clock::SwitchTo()
|
||||
{
|
||||
if (width > size_t(COLS) || height > main_height)
|
||||
{
|
||||
w.Refresh();
|
||||
}
|
||||
ShowMessage("Screen is too small to display clock!");
|
||||
}
|
||||
else if (
|
||||
current_screen != csClock
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
&& current_screen != csTinyTagEditor
|
||||
# endif // HAVE_TAGLIB_H
|
||||
)
|
||||
{
|
||||
CLEAR_FIND_HISTORY;
|
||||
wCurrent = wClock;
|
||||
mPlaylist->Hide();
|
||||
current_screen = csClock;
|
||||
redraw_header = 1;
|
||||
Clock::Prepare();
|
||||
wCurrent->Display();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
14
src/clock.h
14
src/clock.h
@@ -27,11 +27,19 @@
|
||||
|
||||
#ifdef ENABLE_CLOCK
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#include "window.h"
|
||||
|
||||
void InitClock();
|
||||
namespace Clock
|
||||
{
|
||||
void Init();
|
||||
void Resize();
|
||||
void SwitchTo();
|
||||
|
||||
void Update();
|
||||
void Prepare();
|
||||
}
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#endif // ENABLE_CLOCK
|
||||
|
||||
|
||||
@@ -51,5 +51,16 @@ namespace Display
|
||||
void Clock(Window &, const tm *);
|
||||
}
|
||||
|
||||
namespace Refresh
|
||||
{
|
||||
void MediaLibrary();
|
||||
|
||||
void PlaylistEditor();
|
||||
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
void TagEditor();
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
17
src/global.h
17
src/global.h
@@ -49,11 +49,10 @@ namespace Global
|
||||
extern Menu<Buffer> *mTagEditor;
|
||||
extern Menu<string_pair> *mEditorAlbums;
|
||||
extern Menu<string_pair> *mEditorDirs;
|
||||
# endif // HAVE_TAGLIB_H
|
||||
// blah, I use below in conditionals.
|
||||
extern Menu<string_pair> *mEditorLeftCol;
|
||||
extern Menu<std::string> *mEditorTagTypes;
|
||||
extern Menu<MPD::Song> *mEditorTags;
|
||||
# endif // HAVE_TAGLIB_H
|
||||
|
||||
extern Window *wPlaylistEditorActiveCol;
|
||||
extern Menu<std::string> *mPlaylistList;
|
||||
@@ -75,21 +74,22 @@ namespace Global
|
||||
extern int lock_statusbar_delay;
|
||||
|
||||
extern size_t browsed_dir_scroll_begin;
|
||||
extern size_t main_start_y;
|
||||
extern size_t main_height;
|
||||
extern size_t lyrics_scroll_begin;
|
||||
|
||||
extern time_t timer;
|
||||
|
||||
extern std::string browsed_dir;
|
||||
extern std::string editor_browsed_dir;
|
||||
extern std::string editor_highlighted_dir;
|
||||
extern std::string info_title;
|
||||
|
||||
extern NcmpcppScreen current_screen;
|
||||
extern NcmpcppScreen prev_screen;
|
||||
|
||||
# ifdef HAVE_CURL_CURL_H
|
||||
extern pthread_t lyrics_downloader;
|
||||
extern pthread_t artist_info_downloader;
|
||||
extern bool lyrics_ready;
|
||||
extern bool artist_info_ready;
|
||||
extern pthread_mutex_t curl;
|
||||
# endif
|
||||
|
||||
extern bool dont_change_now_playing;
|
||||
@@ -113,6 +113,11 @@ namespace Global
|
||||
|
||||
extern const char *search_mode_normal;
|
||||
extern const char *search_mode_strict;
|
||||
|
||||
extern std::vector<int> vFoundPositions;
|
||||
extern int found_pos;
|
||||
|
||||
extern MPD::Song lyrics_song;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
44
src/help.cpp
44
src/help.cpp
@@ -24,14 +24,48 @@
|
||||
#include "help.h"
|
||||
#include "settings.h"
|
||||
|
||||
using Global::Key;
|
||||
using Global::Mpd;
|
||||
using namespace Global;
|
||||
using std::string;
|
||||
|
||||
Scrollpad *Global::sHelp;
|
||||
|
||||
namespace
|
||||
{
|
||||
string DisplayKeys(int *key, int size = 2)
|
||||
void GetKeybindings(Scrollpad &help);
|
||||
}
|
||||
|
||||
void Help::Init()
|
||||
{
|
||||
sHelp = new Scrollpad(0, main_start_y, COLS, main_height, "", Config.main_color, brNone);
|
||||
sHelp->SetTimeout(ncmpcpp_window_timeout);
|
||||
GetKeybindings(*sHelp);
|
||||
sHelp->Flush();
|
||||
}
|
||||
|
||||
void Help::Resize()
|
||||
{
|
||||
sHelp->Resize(COLS, main_height);
|
||||
}
|
||||
|
||||
void Help::SwitchTo()
|
||||
{
|
||||
if (current_screen != csHelp
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
&& current_screen != csTinyTagEditor
|
||||
# endif // HAVE_TAGLIB_H
|
||||
)
|
||||
{
|
||||
wCurrent = sHelp;
|
||||
wCurrent->Hide();
|
||||
current_screen = csHelp;
|
||||
redraw_header = 1;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
string DisplayKeys(int *key, int size = 2)
|
||||
{
|
||||
bool backspace = 1;
|
||||
string result = "\t";
|
||||
for (int i = 0; i < size; i++)
|
||||
@@ -86,7 +120,6 @@ namespace
|
||||
for (size_t i = result.length(); i <= 12; result += " ", i++) { }
|
||||
result += ": ";
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void GetKeybindings(Scrollpad &help)
|
||||
@@ -193,7 +226,7 @@ void GetKeybindings(Scrollpad &help)
|
||||
help << DisplayKeys(Key.Space) << "Add to playlist song/album/artist's songs\n";
|
||||
help << DisplayKeys(Key.SwitchTagTypeList) << "Tag type list switcher (left column)\n\n\n";
|
||||
|
||||
help << " " << fmtBold << "Keys - Playlist Editorz\n -----------------------------------------\n" << fmtBoldEnd;
|
||||
help << " " << fmtBold << "Keys - Playlist Editor\n -----------------------------------------\n" << fmtBoldEnd;
|
||||
help << DisplayKeys(&Key.VolumeDown[0], 1) << "Previous column\n";
|
||||
help << DisplayKeys(&Key.VolumeUp[0], 1) << "Next column\n";
|
||||
help << DisplayKeys(Key.Enter) << "Add item to playlist and play\n";
|
||||
@@ -218,3 +251,4 @@ void GetKeybindings(Scrollpad &help)
|
||||
# endif // HAVE_TAGLIB_H
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,12 @@
|
||||
|
||||
#include "ncmpcpp.h"
|
||||
|
||||
void GetKeybindings(Scrollpad &);
|
||||
namespace Help
|
||||
{
|
||||
void Init();
|
||||
void Resize();
|
||||
void SwitchTo();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -265,43 +265,6 @@ string FindSharedDir(const string &one, const string &two)
|
||||
return i != string::npos ? result.substr(0, i) : "/";
|
||||
}
|
||||
|
||||
void GetInfo(Song &s, Scrollpad &info)
|
||||
{
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
string path_to_file;
|
||||
if (s.IsFromDB())
|
||||
path_to_file += Config.mpd_music_dir;
|
||||
path_to_file += s.GetFile();
|
||||
TagLib::FileRef f(path_to_file.c_str());
|
||||
if (!f.isNull())
|
||||
s.SetComment(f.tag()->comment().to8Bit(1));
|
||||
# endif // HAVE_TAGLIB_H
|
||||
|
||||
info << fmtBold << Config.color1 << "Filename: " << fmtBoldEnd << Config.color2 << s.GetName() << "\n" << clEnd;
|
||||
info << fmtBold << "Directory: " << fmtBoldEnd << Config.color2 << ShowTagInInfoScreen(s.GetDirectory()) << "\n\n" << clEnd;
|
||||
info << fmtBold << "Length: " << fmtBoldEnd << Config.color2 << s.GetLength() << "\n" << clEnd;
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
if (!f.isNull())
|
||||
{
|
||||
info << fmtBold << "Bitrate: " << fmtBoldEnd << Config.color2 << f.audioProperties()->bitrate() << " kbps\n" << clEnd;
|
||||
info << fmtBold << "Sample rate: " << fmtBoldEnd << Config.color2 << f.audioProperties()->sampleRate() << " Hz\n" << clEnd;
|
||||
info << fmtBold << "Channels: " << fmtBoldEnd << Config.color2 << (f.audioProperties()->channels() == 1 ? "Mono" : "Stereo") << "\n" << clDefault;
|
||||
}
|
||||
else
|
||||
info << clDefault;
|
||||
# endif // HAVE_TAGLIB_H
|
||||
info << fmtBold << "\nTitle: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetTitle());
|
||||
info << fmtBold << "\nArtist: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetArtist());
|
||||
info << fmtBold << "\nAlbum: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetAlbum());
|
||||
info << fmtBold << "\nYear: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetYear());
|
||||
info << fmtBold << "\nTrack: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetTrack());
|
||||
info << fmtBold << "\nGenre: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetGenre());
|
||||
info << fmtBold << "\nComposer: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetComposer());
|
||||
info << fmtBold << "\nPerformer: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetPerformer());
|
||||
info << fmtBold << "\nDisc: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetDisc());
|
||||
info << fmtBold << "\nComment: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetComment());
|
||||
}
|
||||
|
||||
string GetLineValue(string &line, char a, char b, bool once)
|
||||
{
|
||||
int i = 0;
|
||||
@@ -342,21 +305,6 @@ const Buffer &ShowTag(const string &tag)
|
||||
return result;
|
||||
}
|
||||
|
||||
const basic_buffer<my_char_t> &ShowTagInInfoScreen(const string &tag)
|
||||
{
|
||||
# ifdef _UTF8
|
||||
static WBuffer result;
|
||||
result.Clear();
|
||||
if (tag.empty())
|
||||
result << Config.empty_tags_color << ToWString(Config.empty_tag) << clEnd;
|
||||
else
|
||||
result << ToWString(tag);
|
||||
return result;
|
||||
# else
|
||||
return ShowTag(tag);
|
||||
# endif
|
||||
}
|
||||
|
||||
void Scroller(Window &w, const string &string, size_t width, size_t &pos)
|
||||
{
|
||||
std::basic_string<my_char_t> s = TO_WSTRING(string);
|
||||
@@ -401,3 +349,12 @@ void Scroller(Window &w, const string &string, size_t width, size_t &pos)
|
||||
w << s;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CURL_CURL_H
|
||||
size_t write_data(char *buffer, size_t size, size_t nmemb, string data)
|
||||
{
|
||||
size_t result = size * nmemb;
|
||||
data.append(buffer, result);
|
||||
return result;
|
||||
}
|
||||
#endif // HAVE_CURL_CURL_H
|
||||
|
||||
|
||||
@@ -44,16 +44,17 @@ bool Keypressed(int, const int *);
|
||||
|
||||
std::string FindSharedDir(const std::string &, const std::string &);
|
||||
|
||||
void GetInfo(MPD::Song &, Scrollpad &);
|
||||
|
||||
std::string GetLineValue(std::string &, char = '"', char = '"', bool = 0);
|
||||
|
||||
Window &Statusbar();
|
||||
|
||||
const Buffer &ShowTag(const std::string &);
|
||||
const basic_buffer<my_char_t> &ShowTagInInfoScreen(const std::string &);
|
||||
|
||||
void Scroller(Window &, const std::string &, size_t, size_t &);
|
||||
|
||||
#ifdef HAVE_CURL_CURL_H
|
||||
size_t write_data(char *, size_t, size_t, std::string);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
451
src/info.cpp
Normal file
451
src/info.cpp
Normal file
@@ -0,0 +1,451 @@
|
||||
/***************************************************************************
|
||||
* 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 "info.h"
|
||||
|
||||
#ifdef HAVE_CURL_CURL_H
|
||||
# include <fstream>
|
||||
# include <sys/stat.h>
|
||||
# include <pthread.h>
|
||||
# include "curl/curl.h"
|
||||
# include "helpers.h"
|
||||
#endif
|
||||
|
||||
#include "charset.h"
|
||||
#include "global.h"
|
||||
#include "media_library.h"
|
||||
#include "playlist_editor.h"
|
||||
#include "status_checker.h"
|
||||
#include "tag_editor.h"
|
||||
|
||||
using namespace Global;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
Scrollpad *Global::sInfo;
|
||||
|
||||
const string artists_folder = home_folder + "/.ncmpcpp/artists";
|
||||
|
||||
namespace
|
||||
{
|
||||
pthread_t artist_info_downloader;
|
||||
bool artist_info_ready = 0;
|
||||
|
||||
# ifdef HAVE_CURL_CURL_H
|
||||
void *GetArtistInfo(void *);
|
||||
# endif
|
||||
void GetSongInfo(MPD::Song &, Scrollpad &);
|
||||
const basic_buffer<my_char_t> &ShowTagInInfoScreen(const string &);
|
||||
}
|
||||
|
||||
void Info::Init()
|
||||
{
|
||||
sInfo = new Scrollpad(0, main_start_y, COLS, main_height, "", Config.main_color, brNone);
|
||||
sInfo->SetTimeout(ncmpcpp_window_timeout);
|
||||
}
|
||||
|
||||
void Info::Resize()
|
||||
{
|
||||
sInfo->Resize(COLS, main_height);
|
||||
}
|
||||
|
||||
bool Info::Ready()
|
||||
{
|
||||
if (!artist_info_ready)
|
||||
return false;
|
||||
pthread_join(artist_info_downloader, NULL);
|
||||
sInfo->Flush();
|
||||
artist_info_downloader = 0;
|
||||
artist_info_ready = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Info::GetSong()
|
||||
{
|
||||
if (wCurrent == sInfo)
|
||||
{
|
||||
wCurrent->Hide();
|
||||
current_screen = prev_screen;
|
||||
wCurrent = wPrev;
|
||||
// redraw_screen = 1;
|
||||
redraw_header = 1;
|
||||
if (current_screen == csLibrary)
|
||||
{
|
||||
MediaLibrary::Refresh();
|
||||
}
|
||||
else if (current_screen == csPlaylistEditor)
|
||||
{
|
||||
PlaylistEditor::Refresh();
|
||||
}
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
else if (current_screen == csTagEditor)
|
||||
{
|
||||
TagEditor::Refresh();
|
||||
}
|
||||
# endif // HAVE_TAGLIB_H
|
||||
}
|
||||
else if (
|
||||
(wCurrent == mPlaylist && !mPlaylist->Empty())
|
||||
|| (wCurrent == mBrowser && mBrowser->Current().type == MPD::itSong)
|
||||
|| (wCurrent == mSearcher && !mSearcher->Current().first)
|
||||
|| (wCurrent == mLibSongs && !mLibSongs->Empty())
|
||||
|| (wCurrent == mPlaylistEditor && !mPlaylistEditor->Empty())
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
|| (wCurrent == mEditorTags && !mEditorTags->Empty())
|
||||
# endif // HAVE_TAGLIB_H
|
||||
)
|
||||
{
|
||||
MPD::Song *s = 0;
|
||||
size_t id = ((Menu<MPD::Song> *)wCurrent)->Choice();
|
||||
switch (current_screen)
|
||||
{
|
||||
case csPlaylist:
|
||||
s = &mPlaylist->at(id);
|
||||
break;
|
||||
case csBrowser:
|
||||
s = mBrowser->at(id).song;
|
||||
break;
|
||||
case csSearcher:
|
||||
s = mSearcher->at(id).second;
|
||||
break;
|
||||
case csLibrary:
|
||||
s = &mLibSongs->at(id);
|
||||
break;
|
||||
case csPlaylistEditor:
|
||||
s = &mPlaylistEditor->at(id);
|
||||
break;
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
case csTagEditor:
|
||||
s = &mEditorTags->at(id);
|
||||
break;
|
||||
# endif // HAVE_TAGLIB_H
|
||||
default:
|
||||
break;
|
||||
}
|
||||
wPrev = wCurrent;
|
||||
wCurrent = sInfo;
|
||||
prev_screen = current_screen;
|
||||
current_screen = csInfo;
|
||||
redraw_header = 1;
|
||||
info_title = "Song info";
|
||||
sInfo->Clear();
|
||||
GetSongInfo(*s, *sInfo);
|
||||
sInfo->Flush();
|
||||
sInfo->Hide();
|
||||
}
|
||||
}
|
||||
|
||||
void Info::GetArtist()
|
||||
{
|
||||
if (wCurrent == sInfo)
|
||||
{
|
||||
wCurrent->Hide();
|
||||
current_screen = prev_screen;
|
||||
wCurrent = wPrev;
|
||||
// redraw_screen = 1;
|
||||
redraw_header = 1;
|
||||
if (current_screen == csLibrary)
|
||||
{
|
||||
MediaLibrary::Refresh();
|
||||
}
|
||||
else if (current_screen == csPlaylistEditor)
|
||||
{
|
||||
PlaylistEditor::Refresh();
|
||||
}
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
else if (current_screen == csTagEditor)
|
||||
{
|
||||
TagEditor::Refresh();
|
||||
}
|
||||
# endif // HAVE_TAGLIB_H
|
||||
}
|
||||
else if (
|
||||
(wCurrent == mPlaylist && !mPlaylist->Empty())
|
||||
|| (wCurrent == mBrowser && mBrowser->Current().type == MPD::itSong)
|
||||
|| (wCurrent == mSearcher && !mSearcher->Current().first)
|
||||
|| (wCurrent == mLibArtists && !mLibArtists->Empty())
|
||||
|| (wCurrent == mLibSongs && !mLibSongs->Empty())
|
||||
|| (wCurrent == mPlaylistEditor && !mPlaylistEditor->Empty())
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
|| (wCurrent == mEditorTags && !mEditorTags->Empty())
|
||||
# endif // HAVE_TAGLIB_H
|
||||
)
|
||||
{
|
||||
if (artist_info_downloader)
|
||||
{
|
||||
ShowMessage("Artist's info is being downloaded...");
|
||||
return;
|
||||
}
|
||||
|
||||
string *artist = new string();
|
||||
int id = ((Menu<MPD::Song> *)wCurrent)->Choice();
|
||||
switch (current_screen)
|
||||
{
|
||||
case csPlaylist:
|
||||
*artist = mPlaylist->at(id).GetArtist();
|
||||
break;
|
||||
case csBrowser:
|
||||
*artist = mBrowser->at(id).song->GetArtist();
|
||||
break;
|
||||
case csSearcher:
|
||||
*artist = mSearcher->at(id).second->GetArtist();
|
||||
break;
|
||||
case csLibrary:
|
||||
*artist = mLibArtists->at(id);
|
||||
break;
|
||||
case csPlaylistEditor:
|
||||
*artist = mPlaylistEditor->at(id).GetArtist();
|
||||
break;
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
case csTagEditor:
|
||||
*artist = mEditorTags->at(id).GetArtist();
|
||||
break;
|
||||
# endif // HAVE_TAGLIB_H
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!artist->empty())
|
||||
{
|
||||
wPrev = wCurrent;
|
||||
wCurrent = sInfo;
|
||||
prev_screen = current_screen;
|
||||
current_screen = csInfo;
|
||||
redraw_header = 1;
|
||||
info_title = "Artist's info - " + *artist;
|
||||
sInfo->Clear();
|
||||
sInfo->WriteXY(0, 0, 0, "Fetching artist's info...");
|
||||
sInfo->Refresh();
|
||||
if (!artist_info_downloader)
|
||||
{
|
||||
pthread_create(&artist_info_downloader, NULL, GetArtistInfo, artist);
|
||||
}
|
||||
}
|
||||
else
|
||||
delete artist;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
#ifdef HAVE_CURL_CURL_H
|
||||
void *GetArtistInfo(void *ptr)
|
||||
{
|
||||
string *strptr = static_cast<string *>(ptr);
|
||||
string artist = *strptr;
|
||||
delete strptr;
|
||||
|
||||
locale_to_utf(artist);
|
||||
|
||||
string filename = artist + ".txt";
|
||||
ToLower(filename);
|
||||
EscapeUnallowedChars(filename);
|
||||
|
||||
const string fullpath = artists_folder + "/" + filename;
|
||||
mkdir(artists_folder.c_str(), 0755);
|
||||
|
||||
string result;
|
||||
std::ifstream input(fullpath.c_str());
|
||||
|
||||
if (input.is_open())
|
||||
{
|
||||
bool first = 1;
|
||||
string line;
|
||||
while (getline(input, line))
|
||||
{
|
||||
if (!first)
|
||||
*sInfo << "\n";
|
||||
utf_to_locale(line);
|
||||
*sInfo << line;
|
||||
first = 0;
|
||||
}
|
||||
input.close();
|
||||
sInfo->SetFormatting(fmtBold, "\n\nSimilar artists:\n", fmtBoldEnd, 0);
|
||||
sInfo->SetFormatting(Config.color2, "\n * ", clEnd);
|
||||
artist_info_ready = 1;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
CURLcode code;
|
||||
|
||||
char *c_artist = curl_easy_escape(0, artist.c_str(), artist.length());
|
||||
|
||||
string url = "http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist=";
|
||||
url += c_artist;
|
||||
url += "&api_key=d94e5b6e26469a2d1ffae8ef20131b79";
|
||||
|
||||
pthread_mutex_lock(&curl);
|
||||
CURL *info = curl_easy_init();
|
||||
curl_easy_setopt(info, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(info, CURLOPT_WRITEFUNCTION, write_data);
|
||||
curl_easy_setopt(info, CURLOPT_WRITEDATA, &result);
|
||||
curl_easy_setopt(info, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
curl_easy_setopt(info, CURLOPT_NOSIGNAL, 1);
|
||||
code = curl_easy_perform(info);
|
||||
curl_easy_cleanup(info);
|
||||
pthread_mutex_unlock(&curl);
|
||||
|
||||
curl_free(c_artist);
|
||||
|
||||
if (code != CURLE_OK)
|
||||
{
|
||||
*sInfo << "Error while fetching artist's info: " << curl_easy_strerror(code);
|
||||
artist_info_ready = 1;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
size_t a, b;
|
||||
bool save = 1;
|
||||
|
||||
a = result.find("status=\"failed\"");
|
||||
|
||||
if (a != string::npos)
|
||||
{
|
||||
EscapeHtml(result);
|
||||
*sInfo << "Last.fm returned an error message: " << result;
|
||||
artist_info_ready = 1;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
vector<string> similar;
|
||||
for (size_t i = result.find("<name>"); i != string::npos; i = result.find("<name>"))
|
||||
{
|
||||
result[i] = '.';
|
||||
size_t j = result.find("</name>");
|
||||
result[j] = '.';
|
||||
i += 6;
|
||||
similar.push_back(result.substr(i, j-i));
|
||||
EscapeHtml(similar.back());
|
||||
}
|
||||
vector<string> urls;
|
||||
for (size_t i = result.find("<url>"); i != string::npos; i = result.find("<url>"))
|
||||
{
|
||||
result[i] = '.';
|
||||
size_t j = result.find("</url>");
|
||||
result[j] = '.';
|
||||
i += 5;
|
||||
urls.push_back(result.substr(i, j-i));
|
||||
}
|
||||
|
||||
a = result.find("<content>")+9;
|
||||
b = result.find("</content>");
|
||||
|
||||
if (a == b)
|
||||
{
|
||||
result = "No description available for this artist.";
|
||||
save = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
a += 9; // for <![CDATA[
|
||||
b -= 3; // for ]]>
|
||||
result = result.substr(a, b-a);
|
||||
}
|
||||
|
||||
EscapeHtml(result);
|
||||
Trim(result);
|
||||
|
||||
Buffer filebuffer;
|
||||
if (save)
|
||||
filebuffer << result;
|
||||
utf_to_locale(result);
|
||||
*sInfo << result;
|
||||
|
||||
if (save)
|
||||
filebuffer << "\n\nSimilar artists:\n";
|
||||
*sInfo << fmtBold << "\n\nSimilar artists:\n" << fmtBoldEnd;
|
||||
for (size_t i = 1; i < similar.size(); i++)
|
||||
{
|
||||
if (save)
|
||||
filebuffer << "\n * " << similar[i] << " (" << urls[i] << ")";
|
||||
utf_to_locale(similar[i]);
|
||||
utf_to_locale(urls[i]);
|
||||
*sInfo << "\n" << Config.color2 << " * " << clEnd << similar[i] << " (" << urls[i] << ")";
|
||||
}
|
||||
|
||||
if (save)
|
||||
filebuffer << "\n\n" << urls.front();
|
||||
utf_to_locale(urls.front());
|
||||
*sInfo << "\n\n" << urls.front();
|
||||
|
||||
if (save)
|
||||
{
|
||||
std::ofstream output(fullpath.c_str());
|
||||
if (output.is_open())
|
||||
{
|
||||
output << filebuffer.Str();
|
||||
output.close();
|
||||
}
|
||||
}
|
||||
artist_info_ready = 1;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
#endif // HVAE_CURL_CURL_H
|
||||
|
||||
void GetSongInfo(MPD::Song &s, Scrollpad &info)
|
||||
{
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
string path_to_file;
|
||||
if (s.IsFromDB())
|
||||
path_to_file += Config.mpd_music_dir;
|
||||
path_to_file += s.GetFile();
|
||||
TagLib::FileRef f(path_to_file.c_str());
|
||||
if (!f.isNull())
|
||||
s.SetComment(f.tag()->comment().to8Bit(1));
|
||||
# endif // HAVE_TAGLIB_H
|
||||
|
||||
info << fmtBold << Config.color1 << "Filename: " << fmtBoldEnd << Config.color2 << s.GetName() << "\n" << clEnd;
|
||||
info << fmtBold << "Directory: " << fmtBoldEnd << Config.color2 << ShowTagInInfoScreen(s.GetDirectory()) << "\n\n" << clEnd;
|
||||
info << fmtBold << "Length: " << fmtBoldEnd << Config.color2 << s.GetLength() << "\n" << clEnd;
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
if (!f.isNull())
|
||||
{
|
||||
info << fmtBold << "Bitrate: " << fmtBoldEnd << Config.color2 << f.audioProperties()->bitrate() << " kbps\n" << clEnd;
|
||||
info << fmtBold << "Sample rate: " << fmtBoldEnd << Config.color2 << f.audioProperties()->sampleRate() << " Hz\n" << clEnd;
|
||||
info << fmtBold << "Channels: " << fmtBoldEnd << Config.color2 << (f.audioProperties()->channels() == 1 ? "Mono" : "Stereo") << "\n" << clDefault;
|
||||
}
|
||||
else
|
||||
info << clDefault;
|
||||
# endif // HAVE_TAGLIB_H
|
||||
info << fmtBold << "\nTitle: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetTitle());
|
||||
info << fmtBold << "\nArtist: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetArtist());
|
||||
info << fmtBold << "\nAlbum: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetAlbum());
|
||||
info << fmtBold << "\nYear: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetYear());
|
||||
info << fmtBold << "\nTrack: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetTrack());
|
||||
info << fmtBold << "\nGenre: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetGenre());
|
||||
info << fmtBold << "\nComposer: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetComposer());
|
||||
info << fmtBold << "\nPerformer: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetPerformer());
|
||||
info << fmtBold << "\nDisc: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetDisc());
|
||||
info << fmtBold << "\nComment: " << fmtBoldEnd << ShowTagInInfoScreen(s.GetComment());
|
||||
}
|
||||
|
||||
const basic_buffer<my_char_t> &ShowTagInInfoScreen(const string &tag)
|
||||
{
|
||||
# ifdef _UTF8
|
||||
static WBuffer result;
|
||||
result.Clear();
|
||||
if (tag.empty())
|
||||
result << Config.empty_tags_color << ToWString(Config.empty_tag) << clEnd;
|
||||
else
|
||||
result << ToWString(tag);
|
||||
return result;
|
||||
# else
|
||||
return ShowTag(tag);
|
||||
# endif
|
||||
}
|
||||
}
|
||||
41
src/info.h
Normal file
41
src/info.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/***************************************************************************
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef _H_INFO
|
||||
#define _H_INFO
|
||||
|
||||
#include "ncmpcpp.h"
|
||||
#include "mpdpp.h"
|
||||
|
||||
namespace Info
|
||||
{
|
||||
void Init();
|
||||
void Resize();
|
||||
|
||||
bool Ready();
|
||||
|
||||
void GetSong();
|
||||
# ifdef HAVE_CURL_CURL_H
|
||||
void GetArtist();
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
310
src/lyrics.cpp
310
src/lyrics.cpp
@@ -25,231 +25,178 @@
|
||||
#include "global.h"
|
||||
#include "helpers.h"
|
||||
#include "lyrics.h"
|
||||
#include "media_library.h"
|
||||
#include "playlist_editor.h"
|
||||
#include "settings.h"
|
||||
#include "song.h"
|
||||
#include "status_checker.h"
|
||||
#include "tag_editor.h"
|
||||
|
||||
using namespace Global;
|
||||
using std::vector;
|
||||
using std::string;
|
||||
|
||||
const string artists_folder = home_folder + "/.ncmpcpp/artists";
|
||||
Scrollpad *Global::sLyrics;
|
||||
|
||||
MPD::Song Global::lyrics_song;
|
||||
|
||||
const string lyrics_folder = home_folder + "/.lyrics";
|
||||
|
||||
#ifdef HAVE_CURL_CURL_H
|
||||
pthread_mutex_t Global::curl = PTHREAD_MUTEX_INITIALIZER;
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
pthread_mutex_t curl = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_t lyrics_downloader;
|
||||
bool lyrics_ready;
|
||||
|
||||
size_t write_data(char *buffer, size_t size, size_t nmemb, string data)
|
||||
{
|
||||
size_t result = size * nmemb;
|
||||
data.append(buffer, result);
|
||||
return result;
|
||||
}
|
||||
void *GetLyrics(void *);
|
||||
}
|
||||
|
||||
void EscapeHtml(string &s)
|
||||
{
|
||||
bool erase = 0;
|
||||
for (size_t i = s.find("<"); i != string::npos; i = s.find("<"))
|
||||
{
|
||||
size_t j = s.find(">")+1;
|
||||
s.replace(i, j-i, "");
|
||||
}
|
||||
for (size_t i = s.find("'"); i != string::npos; i = s.find("'"))
|
||||
s.replace(i, 6, "'");
|
||||
for (size_t i = s.find("""); i != string::npos; i = s.find("""))
|
||||
s.replace(i, 6, "\"");
|
||||
for (size_t i = s.find("&"); i != string::npos; i = s.find("&"))
|
||||
s.replace(i, 5, "&");
|
||||
for (size_t i = 0; i < s.length(); i++)
|
||||
{
|
||||
if (erase)
|
||||
{
|
||||
s.erase(s.begin()+i);
|
||||
erase = 0;
|
||||
}
|
||||
if (s[i] == 13) // ascii code for windows line ending, get rid of this shit
|
||||
{
|
||||
s[i] = '\n';
|
||||
erase = 1;
|
||||
}
|
||||
else if (s[i] == '\t')
|
||||
s[i] = ' ';
|
||||
}
|
||||
}
|
||||
void Lyrics::Init()
|
||||
{
|
||||
sLyrics = new Scrollpad(0, main_start_y, COLS, main_height, "", Config.main_color, brNone);
|
||||
sLyrics->SetTimeout(ncmpcpp_window_timeout);
|
||||
}
|
||||
|
||||
void Trim(string &s)
|
||||
{
|
||||
if (s.empty())
|
||||
void Lyrics::Resize()
|
||||
{
|
||||
sLyrics->Resize(COLS, main_height);
|
||||
}
|
||||
|
||||
void Lyrics::Update()
|
||||
{
|
||||
if (!reload_lyrics)
|
||||
return;
|
||||
|
||||
size_t b = 0;
|
||||
size_t e = s.length()-1;
|
||||
|
||||
while (!isprint(s[b]))
|
||||
b++;
|
||||
while (!isprint(s[e]))
|
||||
e--;
|
||||
e++;
|
||||
|
||||
if (b != 0 || e != s.length()-1)
|
||||
s = s.substr(b, e-b);
|
||||
}
|
||||
const MPD::Song &s = mPlaylist->at(now_playing);
|
||||
if (!s.GetArtist().empty() && !s.GetTitle().empty())
|
||||
Get();
|
||||
else
|
||||
reload_lyrics = 0;
|
||||
}
|
||||
|
||||
void *GetArtistInfo(void *ptr)
|
||||
bool Lyrics::Ready()
|
||||
{
|
||||
string *strptr = static_cast<string *>(ptr);
|
||||
string artist = *strptr;
|
||||
delete strptr;
|
||||
if (!lyrics_ready)
|
||||
return false;
|
||||
pthread_join(lyrics_downloader, NULL);
|
||||
sLyrics->Flush();
|
||||
lyrics_downloader = 0;
|
||||
lyrics_ready = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
locale_to_utf(artist);
|
||||
|
||||
string filename = artist + ".txt";
|
||||
ToLower(filename);
|
||||
EscapeUnallowedChars(filename);
|
||||
|
||||
const string fullpath = artists_folder + "/" + filename;
|
||||
mkdir(artists_folder.c_str(), 0755);
|
||||
|
||||
string result;
|
||||
std::ifstream input(fullpath.c_str());
|
||||
|
||||
if (input.is_open())
|
||||
void Lyrics::Get()
|
||||
{
|
||||
if (wCurrent == sLyrics && !reload_lyrics)
|
||||
{
|
||||
bool first = 1;
|
||||
string line;
|
||||
while (getline(input, line))
|
||||
wCurrent->Hide();
|
||||
current_screen = prev_screen;
|
||||
wCurrent = wPrev;
|
||||
// redraw_screen = 1;
|
||||
redraw_header = 1;
|
||||
if (current_screen == csLibrary)
|
||||
{
|
||||
if (!first)
|
||||
*sInfo << "\n";
|
||||
utf_to_locale(line);
|
||||
*sInfo << line;
|
||||
first = 0;
|
||||
MediaLibrary::Refresh();
|
||||
}
|
||||
input.close();
|
||||
sInfo->SetFormatting(fmtBold, "\n\nSimilar artists:\n", fmtBoldEnd, 0);
|
||||
sInfo->SetFormatting(Config.color2, "\n * ", clEnd);
|
||||
artist_info_ready = 1;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
CURLcode code;
|
||||
|
||||
char *c_artist = curl_easy_escape(0, artist.c_str(), artist.length());
|
||||
|
||||
string url = "http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist=";
|
||||
url += c_artist;
|
||||
url += "&api_key=d94e5b6e26469a2d1ffae8ef20131b79";
|
||||
|
||||
pthread_mutex_lock(&curl);
|
||||
CURL *info = curl_easy_init();
|
||||
curl_easy_setopt(info, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(info, CURLOPT_WRITEFUNCTION, write_data);
|
||||
curl_easy_setopt(info, CURLOPT_WRITEDATA, &result);
|
||||
curl_easy_setopt(info, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
curl_easy_setopt(info, CURLOPT_NOSIGNAL, 1);
|
||||
code = curl_easy_perform(info);
|
||||
curl_easy_cleanup(info);
|
||||
pthread_mutex_unlock(&curl);
|
||||
|
||||
curl_free(c_artist);
|
||||
|
||||
if (code != CURLE_OK)
|
||||
else if (current_screen == csPlaylistEditor)
|
||||
{
|
||||
*sInfo << "Error while fetching artist's info: " << curl_easy_strerror(code);
|
||||
artist_info_ready = 1;
|
||||
pthread_exit(NULL);
|
||||
PlaylistEditor::Refresh();
|
||||
}
|
||||
|
||||
size_t a, b;
|
||||
bool save = 1;
|
||||
|
||||
a = result.find("status=\"failed\"");
|
||||
|
||||
if (a != string::npos)
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
else if (current_screen == csTagEditor)
|
||||
{
|
||||
EscapeHtml(result);
|
||||
*sInfo << "Last.fm returned an error message: " << result;
|
||||
artist_info_ready = 1;
|
||||
pthread_exit(NULL);
|
||||
TagEditor::Refresh();
|
||||
}
|
||||
|
||||
vector<string> similar;
|
||||
for (size_t i = result.find("<name>"); i != string::npos; i = result.find("<name>"))
|
||||
{
|
||||
result[i] = '.';
|
||||
size_t j = result.find("</name>");
|
||||
result[j] = '.';
|
||||
i += 6;
|
||||
similar.push_back(result.substr(i, j-i));
|
||||
EscapeHtml(similar.back());
|
||||
# endif // HAVE_TAGLIB_H
|
||||
}
|
||||
vector<string> urls;
|
||||
for (size_t i = result.find("<url>"); i != string::npos; i = result.find("<url>"))
|
||||
else if (
|
||||
reload_lyrics
|
||||
|| (wCurrent == mPlaylist && !mPlaylist->Empty())
|
||||
|| (wCurrent == mBrowser && mBrowser->Current().type == MPD::itSong)
|
||||
|| (wCurrent == mSearcher && !mSearcher->Current().first)
|
||||
|| (wCurrent == mLibSongs && !mLibSongs->Empty())
|
||||
|| (wCurrent == mPlaylistEditor && !mPlaylistEditor->Empty())
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
|| (wCurrent == mEditorTags && !mEditorTags->Empty())
|
||||
# endif // HAVE_TAGLIB_H
|
||||
)
|
||||
{
|
||||
result[i] = '.';
|
||||
size_t j = result.find("</url>");
|
||||
result[j] = '.';
|
||||
i += 5;
|
||||
urls.push_back(result.substr(i, j-i));
|
||||
# ifdef HAVE_CURL_CURL_H
|
||||
if (lyrics_downloader)
|
||||
{
|
||||
ShowMessage("Lyrics are being downloaded...");
|
||||
return;
|
||||
}
|
||||
# endif
|
||||
|
||||
a = result.find("<content>")+9;
|
||||
b = result.find("</content>");
|
||||
MPD::Song *s = 0;
|
||||
int id;
|
||||
|
||||
if (a == b)
|
||||
if (reload_lyrics)
|
||||
{
|
||||
result = "No description available for this artist.";
|
||||
save = 0;
|
||||
current_screen = csPlaylist;
|
||||
wCurrent = mPlaylist;
|
||||
reload_lyrics = 0;
|
||||
id = now_playing;
|
||||
}
|
||||
else
|
||||
id = ((Menu<MPD::Song> *)wCurrent)->Choice();
|
||||
|
||||
switch (current_screen)
|
||||
{
|
||||
a += 9; // for <![CDATA[
|
||||
b -= 3; // for ]]>
|
||||
result = result.substr(a, b-a);
|
||||
case csPlaylist:
|
||||
s = &mPlaylist->at(id);
|
||||
break;
|
||||
case csBrowser:
|
||||
s = mBrowser->at(id).song;
|
||||
break;
|
||||
case csSearcher:
|
||||
s = mSearcher->at(id).second;
|
||||
break;
|
||||
case csLibrary:
|
||||
s = &mLibSongs->at(id);
|
||||
break;
|
||||
case csPlaylistEditor:
|
||||
s = &mPlaylistEditor->at(id);
|
||||
break;
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
case csTagEditor:
|
||||
s = &mEditorTags->at(id);
|
||||
break;
|
||||
# endif // HAVE_TAGLIB_H
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
EscapeHtml(result);
|
||||
Trim(result);
|
||||
|
||||
Buffer filebuffer;
|
||||
if (save)
|
||||
filebuffer << result;
|
||||
utf_to_locale(result);
|
||||
*sInfo << result;
|
||||
|
||||
if (save)
|
||||
filebuffer << "\n\nSimilar artists:\n";
|
||||
*sInfo << fmtBold << "\n\nSimilar artists:\n" << fmtBoldEnd;
|
||||
for (size_t i = 1; i < similar.size(); i++)
|
||||
if (!s->GetArtist().empty() && !s->GetTitle().empty())
|
||||
{
|
||||
if (save)
|
||||
filebuffer << "\n * " << similar[i] << " (" << urls[i] << ")";
|
||||
utf_to_locale(similar[i]);
|
||||
utf_to_locale(urls[i]);
|
||||
*sInfo << "\n" << Config.color2 << " * " << clEnd << similar[i] << " (" << urls[i] << ")";
|
||||
lyrics_scroll_begin = 0;
|
||||
lyrics_song = *s;
|
||||
wPrev = wCurrent;
|
||||
prev_screen = current_screen;
|
||||
wCurrent = sLyrics;
|
||||
current_screen = csLyrics;
|
||||
redraw_header = 1;
|
||||
sLyrics->Clear();
|
||||
sLyrics->WriteXY(0, 0, 0, "Fetching lyrics...");
|
||||
sLyrics->Refresh();
|
||||
# ifdef HAVE_CURL_CURL_H
|
||||
if (!lyrics_downloader)
|
||||
{
|
||||
pthread_create(&lyrics_downloader, NULL, GetLyrics, s);
|
||||
}
|
||||
|
||||
if (save)
|
||||
filebuffer << "\n\n" << urls.front();
|
||||
utf_to_locale(urls.front());
|
||||
*sInfo << "\n\n" << urls.front();
|
||||
|
||||
if (save)
|
||||
{
|
||||
std::ofstream output(fullpath.c_str());
|
||||
if (output.is_open())
|
||||
{
|
||||
output << filebuffer.Str();
|
||||
output.close();
|
||||
# else
|
||||
GetLyrics(s);
|
||||
sLyrics->Flush();
|
||||
# endif
|
||||
}
|
||||
}
|
||||
artist_info_ready = 1;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
#ifdef HAVE_CURL_CURL_H
|
||||
|
||||
namespace
|
||||
{
|
||||
bool lyricwiki_not_found(const string &s)
|
||||
@@ -311,6 +258,7 @@ const char *GetLyricsPluginName(int offset)
|
||||
|
||||
#endif // HAVE_CURL_CURL_H
|
||||
|
||||
namespace {
|
||||
void *GetLyrics(void *song)
|
||||
{
|
||||
string artist = static_cast<MPD::Song *>(song)->GetArtist();
|
||||
@@ -415,4 +363,4 @@ void *GetLyrics(void *song)
|
||||
return NULL;
|
||||
# endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
13
src/lyrics.h
13
src/lyrics.h
@@ -26,7 +26,16 @@
|
||||
#ifdef HAVE_CURL_CURL_H
|
||||
# include <pthread.h>
|
||||
# include "curl/curl.h"
|
||||
void *GetArtistInfo(void *);
|
||||
|
||||
namespace Lyrics
|
||||
{
|
||||
void Init();
|
||||
void Resize();
|
||||
void Update();
|
||||
|
||||
bool Ready();
|
||||
void Get();
|
||||
}
|
||||
|
||||
struct LyricsPlugin
|
||||
{
|
||||
@@ -40,7 +49,5 @@ const char *GetLyricsPluginName(int);
|
||||
|
||||
#endif
|
||||
|
||||
void *GetLyrics(void *);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
356
src/media_library.cpp
Normal file
356
src/media_library.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
/***************************************************************************
|
||||
* 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 <algorithm>
|
||||
#include <map>
|
||||
|
||||
#include "charset.h"
|
||||
#include "display.h"
|
||||
#include "helpers.h"
|
||||
#include "global.h"
|
||||
#include "media_library.h"
|
||||
#include "mpdpp.h"
|
||||
#include "status_checker.h"
|
||||
|
||||
using namespace MPD;
|
||||
using namespace Global;
|
||||
using std::string;
|
||||
|
||||
Window *Global::wLibActiveCol;
|
||||
Menu<string> *Global::mLibArtists;
|
||||
Menu<string_pair> *Global::mLibAlbums;
|
||||
Menu<Song> *Global::mLibSongs;
|
||||
|
||||
namespace MediaLibrary
|
||||
{
|
||||
size_t left_col_width;
|
||||
size_t middle_col_width;
|
||||
size_t middle_col_startx;
|
||||
size_t right_col_width;
|
||||
size_t right_col_startx;
|
||||
}
|
||||
|
||||
void MediaLibrary::Init()
|
||||
{
|
||||
left_col_width = COLS/3-1;
|
||||
middle_col_width = COLS/3;
|
||||
middle_col_startx = left_col_width+1;
|
||||
right_col_width = COLS-COLS/3*2-1;
|
||||
right_col_startx = left_col_width+middle_col_width+2;
|
||||
|
||||
mLibArtists = new Menu<string>(0, main_start_y, left_col_width, main_height, IntoStr(Config.media_lib_primary_tag) + "s", Config.main_color, brNone);
|
||||
mLibArtists->HighlightColor(Config.active_column_color);
|
||||
mLibArtists->SetTimeout(ncmpcpp_window_timeout);
|
||||
mLibArtists->SetItemDisplayer(Display::Generic);
|
||||
|
||||
mLibAlbums = new Menu<string_pair>(middle_col_startx, main_start_y, middle_col_width, main_height, "Albums", Config.main_color, brNone);
|
||||
mLibAlbums->HighlightColor(Config.main_highlight_color);
|
||||
mLibAlbums->SetTimeout(ncmpcpp_window_timeout);
|
||||
mLibAlbums->SetItemDisplayer(Display::StringPairs);
|
||||
|
||||
mLibSongs = new Menu<Song>(right_col_startx, main_start_y, right_col_width, main_height, "Songs", Config.main_color, brNone);
|
||||
mLibSongs->HighlightColor(Config.main_highlight_color);
|
||||
mLibSongs->SetTimeout(ncmpcpp_window_timeout);
|
||||
mLibSongs->SetSelectPrefix(&Config.selected_item_prefix);
|
||||
mLibSongs->SetSelectSuffix(&Config.selected_item_suffix);
|
||||
mLibSongs->SetItemDisplayer(Display::Songs);
|
||||
mLibSongs->SetItemDisplayerUserData(&Config.song_library_format);
|
||||
|
||||
wLibActiveCol = mLibArtists;
|
||||
}
|
||||
|
||||
void MediaLibrary::Resize()
|
||||
{
|
||||
left_col_width = COLS/3-1;
|
||||
middle_col_startx = left_col_width+1;
|
||||
middle_col_width = COLS/3;
|
||||
right_col_startx = left_col_width+middle_col_width+2;
|
||||
right_col_width = COLS-COLS/3*2-1;
|
||||
|
||||
mLibArtists->Resize(left_col_width, main_height);
|
||||
mLibAlbums->Resize(middle_col_width, main_height);
|
||||
mLibSongs->Resize(right_col_width, main_height);
|
||||
|
||||
mLibAlbums->MoveTo(middle_col_startx, main_start_y);
|
||||
mLibSongs->MoveTo(right_col_startx, main_start_y);
|
||||
}
|
||||
|
||||
void MediaLibrary::Refresh()
|
||||
{
|
||||
mLibArtists->Display();
|
||||
mvvline(main_start_y, middle_col_startx-1, 0, main_height);
|
||||
mLibAlbums->Display();
|
||||
mvvline(main_start_y, right_col_startx-1, 0, main_height);
|
||||
mLibSongs->Display();
|
||||
if (mLibAlbums->Empty())
|
||||
{
|
||||
mLibAlbums->WriteXY(0, 0, 0, "No albums found.");
|
||||
mLibAlbums->Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void MediaLibrary::SwitchTo()
|
||||
{
|
||||
if (current_screen != csLibrary
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
&& current_screen != csTinyTagEditor
|
||||
# endif // HAVE_TAGLIB_H
|
||||
)
|
||||
{
|
||||
CLEAR_FIND_HISTORY;
|
||||
|
||||
mPlaylist->Hide(); // hack, should be wCurrent, but it doesn't always have 100% width
|
||||
|
||||
// redraw_screen = 1;
|
||||
redraw_header = 1;
|
||||
MediaLibrary::Refresh();
|
||||
|
||||
wCurrent = wLibActiveCol;
|
||||
current_screen = csLibrary;
|
||||
|
||||
UpdateSongList(mLibSongs);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaLibrary::Update()
|
||||
{
|
||||
if (mLibArtists->Empty())
|
||||
{
|
||||
CLEAR_FIND_HISTORY;
|
||||
TagList list;
|
||||
mLibAlbums->Clear(0);
|
||||
mLibSongs->Clear(0);
|
||||
Mpd->GetList(list, Config.media_lib_primary_tag);
|
||||
sort(list.begin(), list.end(), CaseInsensitiveSorting());
|
||||
for (TagList::iterator it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
if (!it->empty())
|
||||
{
|
||||
utf_to_locale(*it);
|
||||
mLibArtists->AddOption(*it);
|
||||
}
|
||||
}
|
||||
mLibArtists->Window::Clear();
|
||||
mLibArtists->Refresh();
|
||||
}
|
||||
|
||||
if (!mLibArtists->Empty() && mLibAlbums->Empty() && mLibSongs->Empty())
|
||||
{
|
||||
mLibAlbums->Reset();
|
||||
TagList list;
|
||||
std::map<string, string, CaseInsensitiveSorting> maplist;
|
||||
locale_to_utf(mLibArtists->Current());
|
||||
if (Config.media_lib_primary_tag == MPD_TAG_ITEM_ARTIST)
|
||||
Mpd->GetAlbums(mLibArtists->Current(), list);
|
||||
else
|
||||
{
|
||||
Mpd->StartSearch(1);
|
||||
Mpd->AddSearch(Config.media_lib_primary_tag, mLibArtists->Current());
|
||||
Mpd->StartFieldSearch(MPD_TAG_ITEM_ALBUM);
|
||||
Mpd->CommitSearch(list);
|
||||
}
|
||||
|
||||
// <mpd-0.14 doesn't support searching for empty tag
|
||||
if (Mpd->Version() > 13)
|
||||
{
|
||||
SongList noalbum_list;
|
||||
Mpd->StartSearch(1);
|
||||
Mpd->AddSearch(Config.media_lib_primary_tag, mLibArtists->Current());
|
||||
Mpd->AddSearch(MPD_TAG_ITEM_ALBUM, "");
|
||||
Mpd->CommitSearch(noalbum_list);
|
||||
if (!noalbum_list.empty())
|
||||
mLibAlbums->AddOption(std::make_pair("<no album>", ""));
|
||||
FreeSongList(noalbum_list);
|
||||
}
|
||||
|
||||
for (TagList::iterator it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
SongList l;
|
||||
Mpd->StartSearch(1);
|
||||
Mpd->AddSearch(Config.media_lib_primary_tag, mLibArtists->Current());
|
||||
Mpd->AddSearch(MPD_TAG_ITEM_ALBUM, *it);
|
||||
Mpd->CommitSearch(l);
|
||||
if (!l.empty() && !l[0]->GetAlbum().empty())
|
||||
{
|
||||
utf_to_locale(*it);
|
||||
l[0]->Localize();
|
||||
maplist[l[0]->toString(Config.media_lib_album_format)] = *it;
|
||||
}
|
||||
FreeSongList(l);
|
||||
}
|
||||
utf_to_locale(mLibArtists->Current());
|
||||
for (std::map<string, string>::const_iterator it = maplist.begin(); it != maplist.end(); it++)
|
||||
mLibAlbums->AddOption(make_pair(it->first, it->second));
|
||||
mLibAlbums->Window::Clear();
|
||||
mLibAlbums->Refresh();
|
||||
}
|
||||
|
||||
if (!mLibArtists->Empty() && wCurrent == mLibAlbums && mLibAlbums->Empty())
|
||||
{
|
||||
mLibAlbums->HighlightColor(Config.main_highlight_color);
|
||||
mLibArtists->HighlightColor(Config.active_column_color);
|
||||
wCurrent = wLibActiveCol = mLibArtists;
|
||||
}
|
||||
|
||||
if (!mLibArtists->Empty() && mLibSongs->Empty())
|
||||
{
|
||||
mLibSongs->Reset();
|
||||
SongList list;
|
||||
|
||||
mLibSongs->Clear(0);
|
||||
Mpd->StartSearch(1);
|
||||
Mpd->AddSearch(Config.media_lib_primary_tag, locale_to_utf_cpy(mLibArtists->Current()));
|
||||
if (mLibAlbums->Empty()) // left for compatibility with <mpd-0.14
|
||||
{
|
||||
mLibAlbums->WriteXY(0, 0, 0, "No albums found.");
|
||||
mLibAlbums->Refresh();
|
||||
}
|
||||
else
|
||||
Mpd->AddSearch(MPD_TAG_ITEM_ALBUM, locale_to_utf_cpy(mLibAlbums->Current().second));
|
||||
Mpd->CommitSearch(list);
|
||||
|
||||
sort(list.begin(), list.end(), SortSongsByTrack);
|
||||
bool bold = 0;
|
||||
|
||||
for (SongList::const_iterator it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
for (size_t j = 0; j < mPlaylist->Size(); j++)
|
||||
{
|
||||
if ((*it)->GetHash() == mPlaylist->at(j).GetHash())
|
||||
{
|
||||
bold = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mLibSongs->AddOption(**it, bold);
|
||||
bold = 0;
|
||||
}
|
||||
FreeSongList(list);
|
||||
mLibSongs->Window::Clear();
|
||||
mLibSongs->Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void MediaLibrary::EnterPressed(bool add_n_play)
|
||||
{
|
||||
SongList list;
|
||||
|
||||
if (!mLibArtists->Empty() && wCurrent == mLibArtists)
|
||||
{
|
||||
Mpd->StartSearch(1);
|
||||
Mpd->AddSearch(Config.media_lib_primary_tag, locale_to_utf_cpy(mLibArtists->Current()));
|
||||
Mpd->CommitSearch(list);
|
||||
for (SongList::const_iterator it = list.begin(); it != list.end(); it++)
|
||||
Mpd->QueueAddSong(**it);
|
||||
if (Mpd->CommitQueue())
|
||||
{
|
||||
string tag_type = IntoStr(Config.media_lib_primary_tag);
|
||||
ToLower(tag_type);
|
||||
ShowMessage("Adding songs of %s \"%s\"", tag_type.c_str(), mLibArtists->Current().c_str());
|
||||
Song *s = &mPlaylist->at(mPlaylist->Size()-list.size());
|
||||
if (s->GetHash() == list[0]->GetHash())
|
||||
{
|
||||
if (add_n_play)
|
||||
Mpd->PlayID(s->GetID());
|
||||
}
|
||||
else
|
||||
ShowMessage("%s", message_part_of_songs_added);
|
||||
}
|
||||
}
|
||||
else if (wCurrent == mLibAlbums)
|
||||
{
|
||||
for (size_t i = 0; i < mLibSongs->Size(); i++)
|
||||
Mpd->QueueAddSong(mLibSongs->at(i));
|
||||
if (Mpd->CommitQueue())
|
||||
{
|
||||
ShowMessage("Adding songs from album \"%s\"", mLibAlbums->Current().second.c_str());
|
||||
Song *s = &mPlaylist->at(mPlaylist->Size()-mLibSongs->Size());
|
||||
if (s->GetHash() == mLibSongs->at(0).GetHash())
|
||||
{
|
||||
if (add_n_play)
|
||||
Mpd->PlayID(s->GetID());
|
||||
}
|
||||
else
|
||||
ShowMessage("%s", message_part_of_songs_added);
|
||||
}
|
||||
}
|
||||
else if (wCurrent == mLibSongs)
|
||||
{
|
||||
if (!mLibSongs->Empty())
|
||||
{
|
||||
block_item_list_update = 1;
|
||||
if (Config.ncmpc_like_songs_adding && mLibSongs->isBold())
|
||||
{
|
||||
long long hash = mLibSongs->Current().GetHash();
|
||||
if (add_n_play)
|
||||
{
|
||||
for (size_t i = 0; i < mPlaylist->Size(); i++)
|
||||
{
|
||||
if (mPlaylist->at(i).GetHash() == hash)
|
||||
{
|
||||
Mpd->Play(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
block_playlist_update = 1;
|
||||
for (size_t i = 0; i < mPlaylist->Size(); i++)
|
||||
{
|
||||
if (mPlaylist->at(i).GetHash() == hash)
|
||||
{
|
||||
Mpd->QueueDeleteSong(i);
|
||||
mPlaylist->DeleteOption(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
Mpd->CommitQueue();
|
||||
mLibSongs->BoldOption(mLibSongs->Choice(), 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Song &s = mLibSongs->Current();
|
||||
int id = Mpd->AddSong(s);
|
||||
if (id >= 0)
|
||||
{
|
||||
ShowMessage("Added to playlist: %s", s.toString(Config.song_status_format).c_str());
|
||||
if (add_n_play)
|
||||
Mpd->PlayID(id);
|
||||
mLibSongs->BoldOption(mLibSongs->Choice(), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FreeSongList(list);
|
||||
if (!add_n_play)
|
||||
{
|
||||
wCurrent->Scroll(wDown);
|
||||
if (wCurrent == mLibArtists)
|
||||
{
|
||||
mLibAlbums->Clear(0);
|
||||
mLibSongs->Clear(0);
|
||||
}
|
||||
else if (wCurrent == mLibAlbums)
|
||||
mLibSongs->Clear(0);
|
||||
}
|
||||
}
|
||||
|
||||
44
src/media_library.h
Normal file
44
src/media_library.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/***************************************************************************
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef _H_MEDIA_LIBRARY
|
||||
#define _H_MEDIA_LIBRARY
|
||||
|
||||
#include "ncmpcpp.h"
|
||||
|
||||
namespace MediaLibrary
|
||||
{
|
||||
void Init();
|
||||
void Resize();
|
||||
void Refresh();
|
||||
void SwitchTo();
|
||||
void Update();
|
||||
|
||||
void EnterPressed(bool = 1);
|
||||
inline void SpacePressed();
|
||||
}
|
||||
|
||||
void MediaLibrary::SpacePressed()
|
||||
{
|
||||
EnterPressed(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
49
src/misc.cpp
49
src/misc.cpp
@@ -160,3 +160,52 @@ void EscapeUnallowedChars(std::string &s)
|
||||
}
|
||||
}
|
||||
|
||||
void EscapeHtml(std::string &s)
|
||||
{
|
||||
bool erase = 0;
|
||||
for (size_t i = s.find("<"); i != std::string::npos; i = s.find("<"))
|
||||
{
|
||||
size_t j = s.find(">")+1;
|
||||
s.replace(i, j-i, "");
|
||||
}
|
||||
for (size_t i = s.find("'"); i != std::string::npos; i = s.find("'"))
|
||||
s.replace(i, 6, "'");
|
||||
for (size_t i = s.find("""); i != std::string::npos; i = s.find("""))
|
||||
s.replace(i, 6, "\"");
|
||||
for (size_t i = s.find("&"); i != std::string::npos; i = s.find("&"))
|
||||
s.replace(i, 5, "&");
|
||||
for (size_t i = 0; i < s.length(); i++)
|
||||
{
|
||||
if (erase)
|
||||
{
|
||||
s.erase(s.begin()+i);
|
||||
erase = 0;
|
||||
}
|
||||
if (s[i] == 13) // ascii code for windows line ending, get rid of this shit
|
||||
{
|
||||
s[i] = '\n';
|
||||
erase = 1;
|
||||
}
|
||||
else if (s[i] == '\t')
|
||||
s[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
void Trim(std::string &s)
|
||||
{
|
||||
if (s.empty())
|
||||
return;
|
||||
|
||||
size_t b = 0;
|
||||
size_t e = s.length()-1;
|
||||
|
||||
while (!isprint(s[b]))
|
||||
b++;
|
||||
while (!isprint(s[e]))
|
||||
e--;
|
||||
e++;
|
||||
|
||||
if (b != 0 || e != s.length()-1)
|
||||
s = s.substr(b, e-b);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,5 +42,9 @@ mpd_TagItems IntoTagItem(char);
|
||||
|
||||
void EscapeUnallowedChars(std::string &);
|
||||
|
||||
void EscapeHtml(std::string &s);
|
||||
|
||||
void Trim(std::string &s);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
1982
src/ncmpcpp.cpp
1982
src/ncmpcpp.cpp
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,12 @@
|
||||
#include "scrollpad.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define CLEAR_FIND_HISTORY \
|
||||
do { \
|
||||
found_pos = -1; \
|
||||
vFoundPositions.clear(); \
|
||||
} while (0)
|
||||
|
||||
typedef std::pair<std::string, std::string> string_pair;
|
||||
|
||||
enum NcmpcppScreen
|
||||
@@ -33,14 +39,20 @@ enum NcmpcppScreen
|
||||
csHelp,
|
||||
csPlaylist,
|
||||
csBrowser,
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
csTinyTagEditor,
|
||||
# endif // HAVE_TAGLIB_H
|
||||
csInfo,
|
||||
csSearcher,
|
||||
csLibrary,
|
||||
csLyrics,
|
||||
csPlaylistEditor,
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
csTagEditor,
|
||||
# endif // HAVE_TAGLIB_H
|
||||
# ifdef ENABLE_CLOCK
|
||||
csClock,
|
||||
# endif // ENABLE_CLOCK
|
||||
csOther
|
||||
};
|
||||
|
||||
@@ -48,5 +60,7 @@ const int ncmpcpp_window_timeout = 500;
|
||||
|
||||
const std::string home_folder = getenv("HOME") ? getenv("HOME") : "";
|
||||
|
||||
const char * const message_part_of_songs_added = "Only part of requested songs' list added to playlist!";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
64
src/playlist.cpp
Normal file
64
src/playlist.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/***************************************************************************
|
||||
* 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 "display.h"
|
||||
#include "global.h"
|
||||
#include "menu.h"
|
||||
#include "playlist.h"
|
||||
#include "song.h"
|
||||
|
||||
using namespace Global;
|
||||
|
||||
Menu<MPD::Song> *Global::mPlaylist;
|
||||
|
||||
void Playlist::Init()
|
||||
{
|
||||
mPlaylist = new Menu<MPD::Song>(0, main_start_y, COLS, main_height, Config.columns_in_playlist ? Display::Columns(Config.song_columns_list_format) : "", Config.main_color, brNone);
|
||||
mPlaylist->SetTimeout(ncmpcpp_window_timeout);
|
||||
mPlaylist->HighlightColor(Config.main_highlight_color);
|
||||
mPlaylist->SetSelectPrefix(&Config.selected_item_prefix);
|
||||
mPlaylist->SetSelectSuffix(&Config.selected_item_suffix);
|
||||
mPlaylist->SetItemDisplayer(Config.columns_in_playlist ? Display::SongsInColumns : Display::Songs);
|
||||
mPlaylist->SetItemDisplayerUserData(Config.columns_in_playlist ? &Config.song_columns_list_format : &Config.song_list_format);
|
||||
}
|
||||
|
||||
void Playlist::Resize()
|
||||
{
|
||||
mPlaylist->Resize(COLS, main_height);
|
||||
mPlaylist->SetTitle(Config.columns_in_playlist ? Display::Columns(Config.song_columns_list_format) : "");
|
||||
}
|
||||
|
||||
void Playlist::SwitchTo()
|
||||
{
|
||||
if (current_screen != csPlaylist
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
&& current_screen != csTinyTagEditor
|
||||
# endif // HAVE_TAGLIB_H
|
||||
)
|
||||
{
|
||||
CLEAR_FIND_HISTORY;
|
||||
wCurrent = mPlaylist;
|
||||
wCurrent->Hide();
|
||||
current_screen = csPlaylist;
|
||||
// redraw_screen = 1;
|
||||
redraw_header = 1;
|
||||
}
|
||||
}
|
||||
|
||||
32
src/playlist.h
Normal file
32
src/playlist.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/***************************************************************************
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef _PLAYLIST_H
|
||||
#define _PLAYLIST_H
|
||||
|
||||
namespace Playlist
|
||||
{
|
||||
void Init();
|
||||
void Resize();
|
||||
void SwitchTo();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
244
src/playlist_editor.cpp
Normal file
244
src/playlist_editor.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
/***************************************************************************
|
||||
* 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 <algorithm>
|
||||
|
||||
#include "charset.h"
|
||||
#include "display.h"
|
||||
#include "global.h"
|
||||
#include "helpers.h"
|
||||
#include "playlist_editor.h"
|
||||
#include "mpdpp.h"
|
||||
#include "status_checker.h"
|
||||
|
||||
using namespace Global;
|
||||
using namespace MPD;
|
||||
using std::string;
|
||||
|
||||
Window *Global::wPlaylistEditorActiveCol;
|
||||
Menu<string> *Global::mPlaylistList;
|
||||
Menu<Song> *Global::mPlaylistEditor;
|
||||
|
||||
namespace PlaylistEditor
|
||||
{
|
||||
size_t left_col_width;
|
||||
size_t right_col_startx;
|
||||
size_t right_col_width;
|
||||
}
|
||||
|
||||
void PlaylistEditor::Init()
|
||||
{
|
||||
left_col_width = COLS/3-1;
|
||||
right_col_startx = left_col_width+1;
|
||||
right_col_width = COLS-left_col_width-1;
|
||||
|
||||
mPlaylistList = new Menu<string>(0, main_start_y, left_col_width, main_height, "Playlists", Config.main_color, brNone);
|
||||
mPlaylistList->HighlightColor(Config.active_column_color);
|
||||
mPlaylistList->SetTimeout(ncmpcpp_window_timeout);
|
||||
mPlaylistList->SetItemDisplayer(Display::Generic);
|
||||
|
||||
mPlaylistEditor = new Menu<Song>(right_col_startx, main_start_y, right_col_width, main_height, "Playlist's content", Config.main_color, brNone);
|
||||
mPlaylistEditor->HighlightColor(Config.main_highlight_color);
|
||||
mPlaylistEditor->SetTimeout(ncmpcpp_window_timeout);
|
||||
mPlaylistEditor->SetSelectPrefix(&Config.selected_item_prefix);
|
||||
mPlaylistEditor->SetSelectSuffix(&Config.selected_item_suffix);
|
||||
mPlaylistEditor->SetItemDisplayer(Display::Songs);
|
||||
mPlaylistEditor->SetItemDisplayerUserData(&Config.song_list_format);
|
||||
|
||||
wPlaylistEditorActiveCol = mPlaylistList;
|
||||
}
|
||||
|
||||
void PlaylistEditor::Resize()
|
||||
{
|
||||
left_col_width = COLS/3-1;
|
||||
right_col_startx = left_col_width+1;
|
||||
right_col_width = COLS-left_col_width-1;
|
||||
|
||||
mPlaylistList->Resize(left_col_width, main_height);
|
||||
mPlaylistEditor->Resize(right_col_width, main_height);
|
||||
|
||||
mPlaylistEditor->MoveTo(right_col_startx, main_start_y);
|
||||
}
|
||||
|
||||
void PlaylistEditor::Refresh()
|
||||
{
|
||||
mPlaylistList->Display();
|
||||
mvvline(main_start_y, right_col_startx-1, 0, main_height);
|
||||
mPlaylistEditor->Display();
|
||||
}
|
||||
|
||||
void PlaylistEditor::SwitchTo()
|
||||
{
|
||||
if (current_screen != csPlaylistEditor
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
&& current_screen != csTinyTagEditor
|
||||
# endif // HAVE_TAGLIB_H
|
||||
)
|
||||
{
|
||||
CLEAR_FIND_HISTORY;
|
||||
|
||||
mPlaylist->Hide(); // hack, should be wCurrent, but it doesn't always have 100% width
|
||||
|
||||
// redraw_screen = 1;
|
||||
redraw_header = 1;
|
||||
PlaylistEditor::Refresh();
|
||||
|
||||
wCurrent = wPlaylistEditorActiveCol;
|
||||
current_screen = csPlaylistEditor;
|
||||
|
||||
UpdateSongList(mPlaylistEditor);
|
||||
}
|
||||
}
|
||||
|
||||
void PlaylistEditor::Update()
|
||||
{
|
||||
if (mPlaylistList->Empty())
|
||||
{
|
||||
mPlaylistEditor->Clear(0);
|
||||
TagList list;
|
||||
Mpd->GetPlaylists(list);
|
||||
sort(list.begin(), list.end(), CaseInsensitiveSorting());
|
||||
for (TagList::iterator it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
utf_to_locale(*it);
|
||||
mPlaylistList->AddOption(*it);
|
||||
}
|
||||
mPlaylistList->Window::Clear();
|
||||
mPlaylistList->Refresh();
|
||||
}
|
||||
|
||||
if (!mPlaylistList->Empty() && mPlaylistEditor->Empty())
|
||||
{
|
||||
mPlaylistEditor->Reset();
|
||||
SongList list;
|
||||
Mpd->GetPlaylistContent(locale_to_utf_cpy(mPlaylistList->Current()), list);
|
||||
if (!list.empty())
|
||||
mPlaylistEditor->SetTitle("Playlist's content (" + IntoStr(list.size()) + " item" + (list.size() == 1 ? ")" : "s)"));
|
||||
else
|
||||
mPlaylistEditor->SetTitle("Playlist's content");
|
||||
bool bold = 0;
|
||||
for (SongList::const_iterator it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
for (size_t j = 0; j < mPlaylist->Size(); j++)
|
||||
{
|
||||
if ((*it)->GetHash() == mPlaylist->at(j).GetHash())
|
||||
{
|
||||
bold = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mPlaylistEditor->AddOption(**it, bold);
|
||||
bold = 0;
|
||||
}
|
||||
FreeSongList(list);
|
||||
mPlaylistEditor->Window::Clear();
|
||||
mPlaylistEditor->Display();
|
||||
}
|
||||
|
||||
if (wCurrent == mPlaylistEditor && mPlaylistEditor->Empty())
|
||||
{
|
||||
mPlaylistEditor->HighlightColor(Config.main_highlight_color);
|
||||
mPlaylistList->HighlightColor(Config.active_column_color);
|
||||
wCurrent = wPlaylistEditorActiveCol = mPlaylistList;
|
||||
}
|
||||
|
||||
if (mPlaylistEditor->Empty())
|
||||
{
|
||||
mPlaylistEditor->WriteXY(0, 0, 0, "Playlist is empty.");
|
||||
mPlaylistEditor->Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void PlaylistEditor::EnterPressed(bool add_n_play)
|
||||
{
|
||||
SongList list;
|
||||
|
||||
if (wCurrent == mPlaylistList && !mPlaylistList->Empty())
|
||||
{
|
||||
Mpd->GetPlaylistContent(locale_to_utf_cpy(mPlaylistList->Current()), list);
|
||||
for (SongList::const_iterator it = list.begin(); it != list.end(); it++)
|
||||
Mpd->QueueAddSong(**it);
|
||||
if (Mpd->CommitQueue())
|
||||
{
|
||||
ShowMessage("Loading playlist %s...", mPlaylistList->Current().c_str());
|
||||
Song &s = mPlaylist->at(mPlaylist->Size()-list.size());
|
||||
if (s.GetHash() == list[0]->GetHash())
|
||||
{
|
||||
if (add_n_play)
|
||||
Mpd->PlayID(s.GetID());
|
||||
}
|
||||
else
|
||||
ShowMessage("%s", message_part_of_songs_added);
|
||||
}
|
||||
}
|
||||
else if (wCurrent == mPlaylistEditor)
|
||||
{
|
||||
if (!mPlaylistEditor->Empty())
|
||||
{
|
||||
block_item_list_update = 1;
|
||||
if (Config.ncmpc_like_songs_adding && mPlaylistEditor->isBold())
|
||||
{
|
||||
long long hash = mPlaylistEditor->Current().GetHash();
|
||||
if (add_n_play)
|
||||
{
|
||||
for (size_t i = 0; i < mPlaylist->Size(); i++)
|
||||
{
|
||||
if (mPlaylist->at(i).GetHash() == hash)
|
||||
{
|
||||
Mpd->Play(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
block_playlist_update = 1;
|
||||
for (size_t i = 0; i < mPlaylist->Size(); i++)
|
||||
{
|
||||
if (mPlaylist->at(i).GetHash() == hash)
|
||||
{
|
||||
Mpd->QueueDeleteSong(i);
|
||||
mPlaylist->DeleteOption(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
Mpd->CommitQueue();
|
||||
mPlaylistEditor->BoldOption(mPlaylistEditor->Choice(), 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Song &s = mPlaylistEditor->at(mPlaylistEditor->Choice());
|
||||
int id = Mpd->AddSong(s);
|
||||
if (id >= 0)
|
||||
{
|
||||
ShowMessage("Added to playlist: %s", s.toString(Config.song_status_format).c_str());
|
||||
if (add_n_play)
|
||||
Mpd->PlayID(id);
|
||||
mPlaylistEditor->BoldOption(mPlaylistEditor->Choice(), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FreeSongList(list);
|
||||
if (!add_n_play)
|
||||
wCurrent->Scroll(wDown);
|
||||
}
|
||||
|
||||
44
src/playlist_editor.h
Normal file
44
src/playlist_editor.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/***************************************************************************
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef _PLAYLIST_EDITOR_H
|
||||
#define _PLAYLIST_EDITOR_H
|
||||
|
||||
#include "ncmpcpp.h"
|
||||
|
||||
namespace PlaylistEditor
|
||||
{
|
||||
void Init();
|
||||
void Resize();
|
||||
void Refresh();
|
||||
void SwitchTo();
|
||||
void Update();
|
||||
|
||||
void EnterPressed(bool = 1);
|
||||
inline void SpacePressed();
|
||||
}
|
||||
|
||||
void PlaylistEditor::SpacePressed()
|
||||
{
|
||||
EnterPressed(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,17 +23,262 @@
|
||||
#include "helpers.h"
|
||||
#include "search_engine.h"
|
||||
#include "settings.h"
|
||||
#include "status_checker.h"
|
||||
|
||||
using namespace MPD;
|
||||
using namespace Global;
|
||||
using std::string;
|
||||
|
||||
Menu< std::pair<Buffer *, Song *> > *Global::mSearcher;
|
||||
|
||||
bool Global::search_match_to_pattern = 1;
|
||||
bool Global::search_case_sensitive = 0;
|
||||
|
||||
const char *Global::search_mode_normal = "Match if tag contains searched phrase";
|
||||
const char *Global::search_mode_strict = "Match only if both values are the same";
|
||||
|
||||
namespace
|
||||
{
|
||||
SearchPattern sought;
|
||||
}
|
||||
|
||||
void SearchEngine::Init()
|
||||
{
|
||||
mSearcher = new Menu< std::pair<Buffer *, Song *> >(0, main_start_y, COLS, main_height, "", Config.main_color, brNone);
|
||||
mSearcher->HighlightColor(Config.main_highlight_color);
|
||||
mSearcher->SetTimeout(ncmpcpp_window_timeout);
|
||||
mSearcher->SetItemDisplayer(Display::SearchEngine);
|
||||
mSearcher->SetSelectPrefix(&Config.selected_item_prefix);
|
||||
mSearcher->SetSelectSuffix(&Config.selected_item_suffix);
|
||||
}
|
||||
|
||||
void SearchEngine::Resize()
|
||||
{
|
||||
mSearcher->Resize(COLS, main_height);
|
||||
}
|
||||
|
||||
void SearchEngine::SwitchTo()
|
||||
{
|
||||
if (current_screen != csSearcher
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
&& current_screen != csTinyTagEditor
|
||||
# endif // HAVE_TAGLIB_H
|
||||
)
|
||||
{
|
||||
CLEAR_FIND_HISTORY;
|
||||
if (mSearcher->Empty())
|
||||
PrepareSearchEngine();
|
||||
wCurrent = mSearcher;
|
||||
wCurrent->Hide();
|
||||
current_screen = csSearcher;
|
||||
// redraw_screen = 1;
|
||||
redraw_header = 1;
|
||||
if (!mSearcher->Back().first)
|
||||
{
|
||||
wCurrent->WriteXY(0, 0, 0, "Updating list...");
|
||||
UpdateFoundList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SearchEngine::EnterPressed()
|
||||
{
|
||||
size_t option = mSearcher->Choice();
|
||||
LockStatusbar();
|
||||
SearchPattern &s = sought;
|
||||
|
||||
if (option <= 12)
|
||||
mSearcher->Current().first->Clear();
|
||||
|
||||
switch (option)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
Statusbar() << fmtBold << "Any: " << fmtBoldEnd;
|
||||
s.Any(wFooter->GetString(s.Any()));
|
||||
*mSearcher->Current().first << fmtBold << "Any: " << fmtBoldEnd << ' ' << ShowTag(s.Any());
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
Statusbar() << fmtBold << "Artist: " << fmtBoldEnd;
|
||||
s.SetArtist(wFooter->GetString(s.GetArtist()));
|
||||
*mSearcher->Current().first << fmtBold << "Artist: " << fmtBoldEnd << ' ' << ShowTag(s.GetArtist());
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
Statusbar() << fmtBold << "Title: " << fmtBoldEnd;
|
||||
s.SetTitle(wFooter->GetString(s.GetTitle()));
|
||||
*mSearcher->Current().first << fmtBold << "Title: " << fmtBoldEnd << ' ' << ShowTag(s.GetTitle());
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
Statusbar() << fmtBold << "Album: " << fmtBoldEnd;
|
||||
s.SetAlbum(wFooter->GetString(s.GetAlbum()));
|
||||
*mSearcher->Current().first << fmtBold << "Album: " << fmtBoldEnd << ' ' << ShowTag(s.GetAlbum());
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
Statusbar() << fmtBold << "Filename: " << fmtBoldEnd;
|
||||
s.SetFile(wFooter->GetString(s.GetFile()));
|
||||
*mSearcher->Current().first << fmtBold << "Filename: " << fmtBoldEnd << ' ' << ShowTag(s.GetFile());
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
Statusbar() << fmtBold << "Composer: " << fmtBoldEnd;
|
||||
s.SetComposer(wFooter->GetString(s.GetComposer()));
|
||||
*mSearcher->Current().first << fmtBold << "Composer: " << fmtBoldEnd << ' ' << ShowTag(s.GetComposer());
|
||||
break;
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
Statusbar() << fmtBold << "Performer: " << fmtBoldEnd;
|
||||
s.SetPerformer(wFooter->GetString(s.GetPerformer()));
|
||||
*mSearcher->Current().first << fmtBold << "Performer:" << fmtBoldEnd << ' ' << ShowTag(s.GetPerformer());
|
||||
break;
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
Statusbar() << fmtBold << "Genre: " << fmtBoldEnd;
|
||||
s.SetGenre(wFooter->GetString(s.GetGenre()));
|
||||
*mSearcher->Current().first << fmtBold << "Genre: " << fmtBoldEnd << ' ' << ShowTag(s.GetGenre());
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
Statusbar() << fmtBold << "Year: " << fmtBoldEnd;
|
||||
s.SetYear(wFooter->GetString(s.GetYear(), 4));
|
||||
*mSearcher->Current().first << fmtBold << "Year: " << fmtBoldEnd << ' ' << ShowTag(s.GetYear());
|
||||
break;
|
||||
}
|
||||
case 9:
|
||||
{
|
||||
Statusbar() << fmtBold << "Comment: " << fmtBoldEnd;
|
||||
s.SetComment(wFooter->GetString(s.GetComment()));
|
||||
*mSearcher->Current().first << fmtBold << "Comment: " << fmtBoldEnd << ' ' << ShowTag(s.GetComment());
|
||||
break;
|
||||
}
|
||||
case 11:
|
||||
{
|
||||
Config.search_in_db = !Config.search_in_db;
|
||||
*mSearcher->Current().first << fmtBold << "Search in:" << fmtBoldEnd << ' ' << (Config.search_in_db ? "Database" : "Current playlist");
|
||||
break;
|
||||
}
|
||||
case 12:
|
||||
{
|
||||
search_match_to_pattern = !search_match_to_pattern;
|
||||
*mSearcher->Current().first << fmtBold << "Search mode:" << fmtBoldEnd << ' ' << (search_match_to_pattern ? search_mode_normal : search_mode_strict);
|
||||
break;
|
||||
}
|
||||
case 13:
|
||||
{
|
||||
search_case_sensitive = !search_case_sensitive;
|
||||
*mSearcher->Current().first << fmtBold << "Case sensitive:" << fmtBoldEnd << ' ' << (search_case_sensitive ? "Yes" : "No");
|
||||
break;
|
||||
}
|
||||
case 15:
|
||||
{
|
||||
ShowMessage("Searching...");
|
||||
Search();
|
||||
if (!mSearcher->Back().first)
|
||||
{
|
||||
if (Config.columns_in_search_engine)
|
||||
mSearcher->SetTitle(Display::Columns(Config.song_columns_list_format));
|
||||
size_t found = mSearcher->Size()-search_engine_static_options;
|
||||
found += 3; // don't count options inserted below
|
||||
mSearcher->InsertSeparator(search_engine_reset_button+1);
|
||||
mSearcher->InsertOption(search_engine_reset_button+2, std::make_pair((Buffer *)0, (Song *)0), 1, 1);
|
||||
mSearcher->at(search_engine_reset_button+2).first = new Buffer();
|
||||
*mSearcher->at(search_engine_reset_button+2).first << Config.color1 << "Search results: " << Config.color2 << "Found " << found << (found > 1 ? " songs" : " song") << clDefault;
|
||||
mSearcher->InsertSeparator(search_engine_reset_button+3);
|
||||
UpdateFoundList();
|
||||
ShowMessage("Searching finished!");
|
||||
for (size_t i = 0; i < search_engine_static_options-4; i++)
|
||||
mSearcher->Static(i, 1);
|
||||
mSearcher->Scroll(wDown);
|
||||
mSearcher->Scroll(wDown);
|
||||
}
|
||||
else
|
||||
ShowMessage("No results found");
|
||||
break;
|
||||
}
|
||||
case 16:
|
||||
{
|
||||
CLEAR_FIND_HISTORY;
|
||||
PrepareSearchEngine();
|
||||
ShowMessage("Search state reset");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
block_item_list_update = 1;
|
||||
if (Config.ncmpc_like_songs_adding && mSearcher->isBold())
|
||||
{
|
||||
long long hash = mSearcher->Current().second->GetHash();
|
||||
for (size_t i = 0; i < mPlaylist->Size(); i++)
|
||||
{
|
||||
if (mPlaylist->at(i).GetHash() == hash)
|
||||
{
|
||||
Mpd->Play(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const Song &s = *mSearcher->Current().second;
|
||||
int id = Mpd->AddSong(s);
|
||||
if (id >= 0)
|
||||
{
|
||||
Mpd->PlayID(id);
|
||||
ShowMessage("Added to playlist: %s", s.toString(Config.song_status_format).c_str());
|
||||
mSearcher->BoldOption(mSearcher->Choice(), 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
UnlockStatusbar();
|
||||
}
|
||||
|
||||
void SearchEngine::SpacePressed()
|
||||
{
|
||||
if (mSearcher->Current().first)
|
||||
return;
|
||||
|
||||
block_item_list_update = 1;
|
||||
if (Config.ncmpc_like_songs_adding && mSearcher->isBold())
|
||||
{
|
||||
block_playlist_update = 1;
|
||||
long long hash = mSearcher->Current().second->GetHash();
|
||||
for (size_t i = 0; i < mPlaylist->Size(); i++)
|
||||
{
|
||||
if (mPlaylist->at(i).GetHash() == hash)
|
||||
{
|
||||
Mpd->QueueDeleteSong(i);
|
||||
mPlaylist->DeleteOption(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
Mpd->CommitQueue();
|
||||
mSearcher->BoldOption(mSearcher->Choice(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
const Song &s = *mSearcher->Current().second;
|
||||
if (Mpd->AddSong(s) != -1)
|
||||
{
|
||||
ShowMessage("Added to playlist: %s", s.toString(Config.song_status_format).c_str());
|
||||
mSearcher->BoldOption(mSearcher->Choice(), 1);
|
||||
}
|
||||
}
|
||||
mSearcher->Scroll(wDown);
|
||||
}
|
||||
|
||||
void UpdateFoundList()
|
||||
{
|
||||
bool bold = 0;
|
||||
@@ -52,8 +297,10 @@ void UpdateFoundList()
|
||||
}
|
||||
}
|
||||
|
||||
void PrepareSearchEngine(SearchPattern &s)
|
||||
void PrepareSearchEngine()
|
||||
{
|
||||
SearchPattern &s = sought;
|
||||
|
||||
for (size_t i = 0; i < mSearcher->Size(); i++)
|
||||
{
|
||||
try
|
||||
@@ -101,11 +348,13 @@ void PrepareSearchEngine(SearchPattern &s)
|
||||
*mSearcher->at(16).first << "Reset";
|
||||
}
|
||||
|
||||
void Search(SearchPattern s)
|
||||
void Search()
|
||||
{
|
||||
if (s.Empty())
|
||||
if (sought.Empty())
|
||||
return;
|
||||
|
||||
SearchPattern s = sought;
|
||||
|
||||
SongList list;
|
||||
if (Config.search_in_db)
|
||||
Mpd->GetDirectoryRecursive("/", list);
|
||||
|
||||
@@ -24,6 +24,16 @@
|
||||
#include "mpdpp.h"
|
||||
#include "ncmpcpp.h"
|
||||
|
||||
namespace SearchEngine
|
||||
{
|
||||
void Init();
|
||||
void Resize();
|
||||
void SwitchTo();
|
||||
|
||||
void EnterPressed();
|
||||
void SpacePressed();
|
||||
}
|
||||
|
||||
class SearchPattern : public MPD::Song
|
||||
{
|
||||
public:
|
||||
@@ -42,8 +52,8 @@ const size_t search_engine_search_button = 15;
|
||||
const size_t search_engine_reset_button = 16;
|
||||
|
||||
void UpdateFoundList();
|
||||
void PrepareSearchEngine(SearchPattern &s);
|
||||
void Search(SearchPattern);
|
||||
void PrepareSearchEngine();
|
||||
void Search();
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -33,11 +33,583 @@
|
||||
#include "display.h"
|
||||
#include "global.h"
|
||||
#include "helpers.h"
|
||||
#include "media_library.h"
|
||||
#include "playlist_editor.h"
|
||||
#include "status_checker.h"
|
||||
|
||||
using namespace Global;
|
||||
using namespace MPD;
|
||||
|
||||
Menu<Buffer> *Global::mTagEditor;
|
||||
|
||||
Window *Global::wTagEditorActiveCol;
|
||||
Menu<string_pair> *Global::mEditorAlbums;
|
||||
Menu<string_pair> *Global::mEditorDirs;
|
||||
Menu<string_pair> *Global::mEditorLeftCol;
|
||||
Menu<string> *Global::mEditorTagTypes;
|
||||
Menu<Song> *Global::mEditorTags;
|
||||
|
||||
void TinyTagEditor::Init()
|
||||
{
|
||||
mTagEditor = new Menu<Buffer>(0, main_start_y, COLS, main_height, "", Config.main_color, brNone);
|
||||
mTagEditor->HighlightColor(Config.main_highlight_color);
|
||||
mTagEditor->SetTimeout(ncmpcpp_window_timeout);
|
||||
mTagEditor->SetItemDisplayer(Display::Generic);
|
||||
}
|
||||
|
||||
void TinyTagEditor::EnterPressed(Song &s)
|
||||
{
|
||||
size_t option = mTagEditor->Choice();
|
||||
LockStatusbar();
|
||||
|
||||
if (option >= 8 && option <= 20)
|
||||
mTagEditor->at(option).Clear();
|
||||
|
||||
switch (option-7)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
Statusbar() << fmtBold << "Title: " << fmtBoldEnd;
|
||||
s.SetTitle(wFooter->GetString(s.GetTitle()));
|
||||
mTagEditor->at(option) << fmtBold << "Title:" << fmtBoldEnd << ' ' << ShowTag(s.GetTitle());
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
Statusbar() << fmtBold << "Artist: " << fmtBoldEnd;
|
||||
s.SetArtist(wFooter->GetString(s.GetArtist()));
|
||||
mTagEditor->at(option) << fmtBold << "Artist:" << fmtBoldEnd << ' ' << ShowTag(s.GetArtist());
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
Statusbar() << fmtBold << "Album: " << fmtBoldEnd;
|
||||
s.SetAlbum(wFooter->GetString(s.GetAlbum()));
|
||||
mTagEditor->at(option) << fmtBold << "Album:" << fmtBoldEnd << ' ' << ShowTag(s.GetAlbum());
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
Statusbar() << fmtBold << "Year: " << fmtBoldEnd;
|
||||
s.SetYear(wFooter->GetString(s.GetYear(), 4));
|
||||
mTagEditor->at(option) << fmtBold << "Year:" << fmtBoldEnd << ' ' << ShowTag(s.GetYear());
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
Statusbar() << fmtBold << "Track: " << fmtBoldEnd;
|
||||
s.SetTrack(wFooter->GetString(s.GetTrack(), 3));
|
||||
mTagEditor->at(option) << fmtBold << "Track:" << fmtBoldEnd << ' ' << ShowTag(s.GetTrack());
|
||||
break;
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
Statusbar() << fmtBold << "Genre: " << fmtBoldEnd;
|
||||
s.SetGenre(wFooter->GetString(s.GetGenre()));
|
||||
mTagEditor->at(option) << fmtBold << "Genre:" << fmtBoldEnd << ' ' << ShowTag(s.GetGenre());
|
||||
break;
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
Statusbar() << fmtBold << "Composer: " << fmtBoldEnd;
|
||||
s.SetComposer(wFooter->GetString(s.GetComposer()));
|
||||
mTagEditor->at(option) << fmtBold << "Composer:" << fmtBoldEnd << ' ' << ShowTag(s.GetComposer());
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
Statusbar() << fmtBold << "Performer: " << fmtBoldEnd;
|
||||
s.SetPerformer(wFooter->GetString(s.GetPerformer()));
|
||||
mTagEditor->at(option) << fmtBold << "Performer:" << fmtBoldEnd << ' ' << ShowTag(s.GetPerformer());
|
||||
break;
|
||||
}
|
||||
case 9:
|
||||
{
|
||||
Statusbar() << fmtBold << "Disc: " << fmtBoldEnd;
|
||||
s.SetDisc(wFooter->GetString(s.GetDisc()));
|
||||
mTagEditor->at(option) << fmtBold << "Disc:" << fmtBoldEnd << ' ' << ShowTag(s.GetDisc());
|
||||
break;
|
||||
}
|
||||
case 10:
|
||||
{
|
||||
Statusbar() << fmtBold << "Comment: " << fmtBoldEnd;
|
||||
s.SetComment(wFooter->GetString(s.GetComment()));
|
||||
mTagEditor->at(option) << fmtBold << "Comment:" << fmtBoldEnd << ' ' << ShowTag(s.GetComment());
|
||||
break;
|
||||
}
|
||||
case 12:
|
||||
{
|
||||
Statusbar() << fmtBold << "Filename: " << fmtBoldEnd;
|
||||
string filename = s.GetNewName().empty() ? s.GetName() : s.GetNewName();
|
||||
size_t dot = filename.rfind(".");
|
||||
string extension = filename.substr(dot);
|
||||
filename = filename.substr(0, dot);
|
||||
string new_name = wFooter->GetString(filename);
|
||||
s.SetNewName(new_name + extension);
|
||||
mTagEditor->at(option) << fmtBold << "Filename:" << fmtBoldEnd << ' ' << (s.GetNewName().empty() ? s.GetName() : s.GetNewName());
|
||||
break;
|
||||
}
|
||||
case 14:
|
||||
{
|
||||
ShowMessage("Updating tags...");
|
||||
if (WriteTags(s))
|
||||
{
|
||||
ShowMessage("Tags updated!");
|
||||
if (s.IsFromDB())
|
||||
{
|
||||
Mpd->UpdateDirectory(locale_to_utf_cpy(s.GetDirectory()));
|
||||
if (prev_screen == csSearcher)
|
||||
*mSearcher->Current().second = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (wPrev == mPlaylist)
|
||||
mPlaylist->Current() = s;
|
||||
else if (wPrev == mBrowser)
|
||||
*mBrowser->Current().song = s;
|
||||
}
|
||||
}
|
||||
else
|
||||
ShowMessage("Error writing tags!");
|
||||
}
|
||||
case 15:
|
||||
{
|
||||
wCurrent->Clear();
|
||||
wCurrent = wPrev;
|
||||
current_screen = prev_screen;
|
||||
redraw_header = 1;
|
||||
if (current_screen == csLibrary)
|
||||
{
|
||||
MediaLibrary::Refresh();
|
||||
}
|
||||
else if (current_screen == csPlaylistEditor)
|
||||
{
|
||||
PlaylistEditor::Refresh();
|
||||
}
|
||||
else if (current_screen == csTagEditor)
|
||||
{
|
||||
TagEditor::Refresh();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
UnlockStatusbar();
|
||||
}
|
||||
|
||||
namespace TagEditor
|
||||
{
|
||||
const size_t middle_col_width = 26;
|
||||
size_t left_col_width;
|
||||
size_t middle_col_startx;
|
||||
size_t right_col_width;
|
||||
size_t right_col_startx;
|
||||
}
|
||||
|
||||
void TagEditor::Init()
|
||||
{
|
||||
left_col_width = COLS/2-middle_col_width/2;
|
||||
middle_col_startx = left_col_width+1;
|
||||
right_col_width = COLS-left_col_width-middle_col_width-2;
|
||||
right_col_startx = left_col_width+middle_col_width+2;
|
||||
|
||||
mEditorAlbums = new Menu<string_pair>(0, main_start_y, left_col_width, main_height, "Albums", Config.main_color, brNone);
|
||||
mEditorAlbums->HighlightColor(Config.active_column_color);
|
||||
mEditorAlbums->SetTimeout(ncmpcpp_window_timeout);
|
||||
mEditorAlbums->SetItemDisplayer(Display::StringPairs);
|
||||
|
||||
mEditorDirs = new Menu<string_pair>(0, main_start_y, left_col_width, main_height, "Directories", Config.main_color, brNone);
|
||||
mEditorDirs->HighlightColor(Config.active_column_color);
|
||||
mEditorDirs->SetTimeout(ncmpcpp_window_timeout);
|
||||
mEditorDirs->SetItemDisplayer(Display::StringPairs);
|
||||
mEditorLeftCol = Config.albums_in_tag_editor ? mEditorAlbums : mEditorDirs;
|
||||
|
||||
mEditorTagTypes = new Menu<string>(middle_col_startx, main_start_y, middle_col_width, main_height, "Tag types", Config.main_color, brNone);
|
||||
mEditorTagTypes->HighlightColor(Config.main_highlight_color);
|
||||
mEditorTagTypes->SetTimeout(ncmpcpp_window_timeout);
|
||||
mEditorTagTypes->SetItemDisplayer(Display::Generic);
|
||||
|
||||
mEditorTags = new Menu<Song>(right_col_startx, main_start_y, right_col_width, main_height, "Tags", Config.main_color, brNone);
|
||||
mEditorTags->HighlightColor(Config.main_highlight_color);
|
||||
mEditorTags->SetTimeout(ncmpcpp_window_timeout);
|
||||
mEditorTags->SetSelectPrefix(&Config.selected_item_prefix);
|
||||
mEditorTags->SetSelectSuffix(&Config.selected_item_suffix);
|
||||
mEditorTags->SetItemDisplayer(Display::Tags);
|
||||
mEditorTags->SetItemDisplayerUserData(mEditorTagTypes);
|
||||
|
||||
wTagEditorActiveCol = mEditorLeftCol;
|
||||
}
|
||||
|
||||
void TagEditor::Resize()
|
||||
{
|
||||
left_col_width = COLS/2-middle_col_width/2;
|
||||
middle_col_startx = left_col_width+1;
|
||||
right_col_width = COLS-left_col_width-middle_col_width-2;
|
||||
right_col_startx = left_col_width+middle_col_width+2;
|
||||
|
||||
mEditorAlbums->Resize(left_col_width, main_height);
|
||||
mEditorDirs->Resize(left_col_width, main_height);
|
||||
mEditorTagTypes->Resize(middle_col_width, main_height);
|
||||
mEditorTags->Resize(right_col_width, main_height);
|
||||
|
||||
mEditorTagTypes->MoveTo(middle_col_startx, main_start_y);
|
||||
mEditorTags->MoveTo(right_col_startx, main_start_y);
|
||||
}
|
||||
|
||||
void TagEditor::Refresh()
|
||||
{
|
||||
mEditorLeftCol->Display();
|
||||
mvvline(main_start_y, middle_col_startx-1, 0, main_height);
|
||||
mEditorTagTypes->Display();
|
||||
mvvline(main_start_y, right_col_startx-1, 0, main_height);
|
||||
mEditorTags->Display();
|
||||
}
|
||||
|
||||
void TagEditor::SwitchTo()
|
||||
{
|
||||
if (current_screen != csTagEditor && current_screen != csTinyTagEditor)
|
||||
{
|
||||
CLEAR_FIND_HISTORY;
|
||||
|
||||
mPlaylist->Hide(); // hack, should be wCurrent, but it doesn't always have 100% width
|
||||
|
||||
// redraw_screen = 1;
|
||||
redraw_header = 1;
|
||||
TagEditor::Refresh();
|
||||
|
||||
if (mEditorTagTypes->Empty())
|
||||
{
|
||||
mEditorTagTypes->AddOption("Title");
|
||||
mEditorTagTypes->AddOption("Artist");
|
||||
mEditorTagTypes->AddOption("Album");
|
||||
mEditorTagTypes->AddOption("Year");
|
||||
mEditorTagTypes->AddOption("Track");
|
||||
mEditorTagTypes->AddOption("Genre");
|
||||
mEditorTagTypes->AddOption("Composer");
|
||||
mEditorTagTypes->AddOption("Performer");
|
||||
mEditorTagTypes->AddOption("Disc");
|
||||
mEditorTagTypes->AddOption("Comment");
|
||||
mEditorTagTypes->AddSeparator();
|
||||
mEditorTagTypes->AddOption("Filename");
|
||||
mEditorTagTypes->AddSeparator();
|
||||
mEditorTagTypes->AddOption("Options", 1, 1, 0);
|
||||
mEditorTagTypes->AddSeparator();
|
||||
mEditorTagTypes->AddOption("Reset");
|
||||
mEditorTagTypes->AddOption("Save");
|
||||
mEditorTagTypes->AddSeparator();
|
||||
mEditorTagTypes->AddOption("Capitalize First Letters");
|
||||
mEditorTagTypes->AddOption("lower all letters");
|
||||
}
|
||||
|
||||
wCurrent = wTagEditorActiveCol;
|
||||
current_screen = csTagEditor;
|
||||
}
|
||||
}
|
||||
|
||||
void TagEditor::Update()
|
||||
{
|
||||
if (mEditorLeftCol->Empty())
|
||||
{
|
||||
CLEAR_FIND_HISTORY;
|
||||
mEditorLeftCol->Window::Clear();
|
||||
mEditorTags->Clear();
|
||||
TagList list;
|
||||
if (Config.albums_in_tag_editor)
|
||||
{
|
||||
std::map<string, string, CaseInsensitiveSorting> maplist;
|
||||
mEditorAlbums->WriteXY(0, 0, 0, "Fetching albums' list...");
|
||||
Mpd->GetAlbums("", list);
|
||||
for (TagList::const_iterator it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
SongList l;
|
||||
Mpd->StartSearch(1);
|
||||
Mpd->AddSearch(MPD_TAG_ITEM_ALBUM, *it);
|
||||
Mpd->CommitSearch(l);
|
||||
if (!l.empty())
|
||||
{
|
||||
l[0]->Localize();
|
||||
maplist[l[0]->toString(Config.tag_editor_album_format)] = *it;
|
||||
}
|
||||
FreeSongList(l);
|
||||
}
|
||||
for (std::map<string, string>::const_iterator it = maplist.begin(); it != maplist.end(); it++)
|
||||
mEditorAlbums->AddOption(make_pair(it->first, it->second));
|
||||
}
|
||||
else
|
||||
{
|
||||
int highlightme = -1;
|
||||
Mpd->GetDirectories(editor_browsed_dir, list);
|
||||
sort(list.begin(), list.end(), CaseInsensitiveSorting());
|
||||
if (editor_browsed_dir != "/")
|
||||
{
|
||||
size_t slash = editor_browsed_dir.rfind("/");
|
||||
string parent = slash != string::npos ? editor_browsed_dir.substr(0, slash) : "/";
|
||||
mEditorDirs->AddOption(make_pair("[..]", parent));
|
||||
}
|
||||
else
|
||||
{
|
||||
mEditorDirs->AddOption(make_pair(".", "/"));
|
||||
}
|
||||
for (TagList::const_iterator it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
size_t slash = it->rfind("/");
|
||||
string to_display = slash != string::npos ? it->substr(slash+1) : *it;
|
||||
utf_to_locale(to_display);
|
||||
mEditorDirs->AddOption(make_pair(to_display, *it));
|
||||
if (*it == editor_highlighted_dir)
|
||||
highlightme = mEditorDirs->Size()-1;
|
||||
}
|
||||
if (highlightme != -1)
|
||||
mEditorDirs->Highlight(highlightme);
|
||||
}
|
||||
mEditorLeftCol->Display();
|
||||
mEditorTagTypes->Refresh();
|
||||
}
|
||||
|
||||
if (mEditorTags->Empty())
|
||||
{
|
||||
mEditorTags->Reset();
|
||||
SongList list;
|
||||
if (Config.albums_in_tag_editor)
|
||||
{
|
||||
Mpd->StartSearch(1);
|
||||
Mpd->AddSearch(MPD_TAG_ITEM_ALBUM, mEditorAlbums->Current().second);
|
||||
Mpd->CommitSearch(list);
|
||||
sort(list.begin(), list.end(), CaseInsensitiveSorting());
|
||||
for (SongList::iterator it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
(*it)->Localize();
|
||||
mEditorTags->AddOption(**it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Mpd->GetSongs(mEditorDirs->Current().second, list);
|
||||
sort(list.begin(), list.end(), CaseInsensitiveSorting());
|
||||
for (SongList::const_iterator it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
(*it)->Localize();
|
||||
mEditorTags->AddOption(**it);
|
||||
}
|
||||
}
|
||||
FreeSongList(list);
|
||||
mEditorTags->Window::Clear();
|
||||
mEditorTags->Refresh();
|
||||
}
|
||||
|
||||
if (/*redraw_screen && */wCurrent == mEditorTagTypes && mEditorTagTypes->Choice() < 13)
|
||||
{
|
||||
mEditorTags->Refresh();
|
||||
// redraw_screen = 0;
|
||||
}
|
||||
else if (mEditorTagTypes->Choice() >= 13)
|
||||
mEditorTags->Window::Clear();
|
||||
}
|
||||
|
||||
void TagEditor::EnterPressed()
|
||||
{
|
||||
if (wCurrent == mEditorDirs)
|
||||
{
|
||||
TagList test;
|
||||
Mpd->GetDirectories(mEditorLeftCol->Current().second, test);
|
||||
if (!test.empty())
|
||||
{
|
||||
editor_highlighted_dir = editor_browsed_dir;
|
||||
editor_browsed_dir = mEditorLeftCol->Current().second;
|
||||
mEditorLeftCol->Clear(0);
|
||||
mEditorLeftCol->Reset();
|
||||
}
|
||||
else
|
||||
ShowMessage("No subdirs found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mEditorTags->Empty()) // we need songs to deal with, don't we?
|
||||
return;
|
||||
|
||||
// if there are selected songs, perform operations only on them
|
||||
SongList list;
|
||||
if (mEditorTags->hasSelected())
|
||||
{
|
||||
vector<size_t> selected;
|
||||
mEditorTags->GetSelected(selected);
|
||||
for (vector<size_t>::const_iterator it = selected.begin(); it != selected.end(); it++)
|
||||
list.push_back(&mEditorTags->at(*it));
|
||||
}
|
||||
else
|
||||
for (size_t i = 0; i < mEditorTags->Size(); i++)
|
||||
list.push_back(&mEditorTags->at(i));
|
||||
|
||||
SongGetFunction get = 0;
|
||||
SongSetFunction set = 0;
|
||||
|
||||
size_t id = mEditorTagTypes->RealChoice();
|
||||
switch (id)
|
||||
{
|
||||
case 0:
|
||||
get = &Song::GetTitle;
|
||||
set = &Song::SetTitle;
|
||||
break;
|
||||
case 1:
|
||||
get = &Song::GetArtist;
|
||||
set = &Song::SetArtist;
|
||||
break;
|
||||
case 2:
|
||||
get = &Song::GetAlbum;
|
||||
set = &Song::SetAlbum;
|
||||
break;
|
||||
case 3:
|
||||
get = &Song::GetYear;
|
||||
set = &Song::SetYear;
|
||||
break;
|
||||
case 4:
|
||||
get = &Song::GetTrack;
|
||||
set = &Song::SetTrack;
|
||||
if (wCurrent == mEditorTagTypes)
|
||||
{
|
||||
LockStatusbar();
|
||||
Statusbar() << "Number tracks? [y/n] ";
|
||||
curs_set(1);
|
||||
int in = 0;
|
||||
do
|
||||
{
|
||||
TraceMpdStatus();
|
||||
wFooter->ReadKey(in);
|
||||
}
|
||||
while (in != 'y' && in != 'n');
|
||||
if (in == 'y')
|
||||
{
|
||||
int i = 1;
|
||||
for (SongList::iterator it = list.begin(); it != list.end(); it++, i++)
|
||||
(*it)->SetTrack(i);
|
||||
ShowMessage("Tracks numbered!");
|
||||
}
|
||||
else
|
||||
ShowMessage("Aborted!");
|
||||
curs_set(0);
|
||||
UnlockStatusbar();
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
get = &Song::GetGenre;
|
||||
set = &Song::SetGenre;
|
||||
break;
|
||||
case 6:
|
||||
get = &Song::GetComposer;
|
||||
set = &Song::SetComposer;
|
||||
break;
|
||||
case 7:
|
||||
get = &Song::GetPerformer;
|
||||
set = &Song::SetPerformer;
|
||||
break;
|
||||
case 8:
|
||||
get = &Song::GetDisc;
|
||||
set = &Song::SetDisc;
|
||||
break;
|
||||
case 9:
|
||||
get = &Song::GetComment;
|
||||
set = &Song::SetComment;
|
||||
break;
|
||||
case 10:
|
||||
{
|
||||
if (wCurrent == mEditorTagTypes)
|
||||
{
|
||||
current_screen = csOther;
|
||||
__deal_with_filenames(list);
|
||||
current_screen = csTagEditor;
|
||||
TagEditor::Refresh();
|
||||
}
|
||||
else if (wCurrent == mEditorTags)
|
||||
{
|
||||
Song &s = mEditorTags->Current();
|
||||
string old_name = s.GetNewName().empty() ? s.GetName() : s.GetNewName();
|
||||
size_t last_dot = old_name.rfind(".");
|
||||
string extension = old_name.substr(last_dot);
|
||||
old_name = old_name.substr(0, last_dot);
|
||||
LockStatusbar();
|
||||
Statusbar() << fmtBold << "New filename: " << fmtBoldEnd;
|
||||
string new_name = wFooter->GetString(old_name);
|
||||
UnlockStatusbar();
|
||||
if (!new_name.empty() && new_name != old_name)
|
||||
s.SetNewName(new_name + extension);
|
||||
mEditorTags->Scroll(wDown);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 11: // reset
|
||||
{
|
||||
mEditorTags->Clear(0);
|
||||
ShowMessage("Changes reset");
|
||||
return;
|
||||
}
|
||||
case 12: // save
|
||||
{
|
||||
bool success = 1;
|
||||
ShowMessage("Writing changes...");
|
||||
for (SongList::iterator it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
ShowMessage("Writing tags in '%s'...", (*it)->GetName().c_str());
|
||||
if (!WriteTags(**it))
|
||||
{
|
||||
ShowMessage("Error writing tags in '%s'!", (*it)->GetFile().c_str());
|
||||
success = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (success)
|
||||
{
|
||||
ShowMessage("Tags updated!");
|
||||
mEditorTagTypes->HighlightColor(Config.main_highlight_color);
|
||||
mEditorTagTypes->Reset();
|
||||
wCurrent->Refresh();
|
||||
wCurrent = mEditorLeftCol;
|
||||
mEditorLeftCol->HighlightColor(Config.active_column_color);
|
||||
Mpd->UpdateDirectory(FindSharedDir(mEditorTags));
|
||||
}
|
||||
else
|
||||
mEditorTags->Clear(0);
|
||||
return;
|
||||
}
|
||||
case 13: // capitalize first letters
|
||||
{
|
||||
ShowMessage("Processing...");
|
||||
for (SongList::iterator it = list.begin(); it != list.end(); it++)
|
||||
CapitalizeFirstLetters(**it);
|
||||
ShowMessage("Done!");
|
||||
break;
|
||||
}
|
||||
case 14: // lower all letters
|
||||
{
|
||||
ShowMessage("Processing...");
|
||||
for (SongList::iterator it = list.begin(); it != list.end(); it++)
|
||||
LowerAllLetters(**it);
|
||||
ShowMessage("Done!");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (wCurrent == mEditorTagTypes && id != 0 && id != 4 && set != NULL)
|
||||
{
|
||||
LockStatusbar();
|
||||
Statusbar() << fmtBold << mEditorTagTypes->Current() << fmtBoldEnd << ": ";
|
||||
string new_tag = wFooter->GetString((mEditorTags->Current().*get)());
|
||||
UnlockStatusbar();
|
||||
for (SongList::iterator it = list.begin(); it != list.end(); it++)
|
||||
(**it.*set)(new_tag);
|
||||
}
|
||||
else if (wCurrent == mEditorTags && set != NULL)
|
||||
{
|
||||
LockStatusbar();
|
||||
Statusbar() << fmtBold << mEditorTagTypes->Current() << fmtBoldEnd << ": ";
|
||||
string new_tag = wFooter->GetString((mEditorTags->Current().*get)());
|
||||
UnlockStatusbar();
|
||||
if (new_tag != (mEditorTags->Current().*get)())
|
||||
(mEditorTags->Current().*set)(new_tag);
|
||||
mEditorTags->Scroll(wDown);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
const string patterns_list_file = config_dir + "patterns.list";
|
||||
|
||||
@@ -32,6 +32,25 @@
|
||||
#include "mpdpp.h"
|
||||
#include "settings.h"
|
||||
|
||||
namespace TinyTagEditor
|
||||
{
|
||||
void Init();
|
||||
|
||||
void EnterPressed(MPD::Song &);
|
||||
}
|
||||
|
||||
namespace TagEditor
|
||||
{
|
||||
void Init();
|
||||
void Resize();
|
||||
void Refresh();
|
||||
void SwitchTo();
|
||||
|
||||
void Update();
|
||||
|
||||
void EnterPressed();
|
||||
}
|
||||
|
||||
typedef void (MPD::Song::*SongSetFunction)(const std::string &);
|
||||
typedef std::string (MPD::Song::*SongGetFunction)() const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user