new feature: apply filter to screen (Ctrl-F)
if one applies a filter, only items that contain this filter will be displayed. works on all screens.
This commit is contained in:
2
doc/keys
2
doc/keys
@@ -82,6 +82,8 @@
|
||||
#
|
||||
#key_update_db = 'u'
|
||||
#
|
||||
#key_apply_filter = 6
|
||||
#
|
||||
#key_find_forward = '/'
|
||||
#
|
||||
#key_find_backward = '?'
|
||||
|
||||
@@ -48,6 +48,7 @@ void Browser::Init()
|
||||
w->SetSelectPrefix(&Config.selected_item_prefix);
|
||||
w->SetSelectSuffix(&Config.selected_item_suffix);
|
||||
w->SetItemDisplayer(Display::Items);
|
||||
w->SetGetStringFunction(ItemToString);
|
||||
}
|
||||
|
||||
void Browser::Resize()
|
||||
@@ -437,3 +438,31 @@ void Browser::UpdateItemList()
|
||||
w->Refresh();
|
||||
}
|
||||
|
||||
std::string Browser::ItemToString(const MPD::Item &item, void *)
|
||||
{
|
||||
switch (item.type)
|
||||
{
|
||||
case MPD::itDirectory:
|
||||
{
|
||||
if (item.song)
|
||||
return "[..]";
|
||||
return "[" + ExtractTopDirectory(item.name) + "]";
|
||||
}
|
||||
case MPD::itSong:
|
||||
{
|
||||
if (!Config.columns_in_browser)
|
||||
return item.song->toString(Config.song_list_format);
|
||||
else
|
||||
return Playlist::SongInColumnsToString(*item.song, &Config.song_columns_list_format);
|
||||
}
|
||||
case MPD::itPlaylist:
|
||||
{
|
||||
return Config.browser_playlist_prefix.Str() + item.name;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,8 @@ class Browser : public Screen< Menu<MPD::Item> >
|
||||
virtual void ReverseSelection();
|
||||
virtual void GetSelectedSongs(MPD::SongList &);
|
||||
|
||||
virtual void ApplyFilter(const std::string &s) { w->ApplyFilter(s, itsBrowsedDir == "/" ? 0 : 1); }
|
||||
|
||||
virtual List *GetList() { return w; }
|
||||
|
||||
const std::string &CurrentDir() { return itsBrowsedDir; }
|
||||
@@ -53,6 +55,8 @@ class Browser : public Screen< Menu<MPD::Item> >
|
||||
void UpdateItemList();
|
||||
|
||||
protected:
|
||||
static std::string ItemToString(const MPD::Item &, void *);
|
||||
|
||||
size_t itsScrollBeginning;
|
||||
|
||||
std::string itsBrowsedDir;
|
||||
|
||||
@@ -168,6 +168,7 @@ void Help::GetKeybindings()
|
||||
*w << DisplayKeys(Key.SetCrossfade) << "Set crossfade\n";
|
||||
*w << DisplayKeys(Key.UpdateDB) << "Start a music database update\n\n";
|
||||
|
||||
*w << DisplayKeys(Key.ApplyFilter) << "Apply filter\n";
|
||||
*w << DisplayKeys(Key.FindForward) << "Forward find\n";
|
||||
*w << DisplayKeys(Key.FindBackward) << "Backward find\n";
|
||||
*w << DisplayKeys(Key.PrevFoundPosition) << "Go to previous found position\n";
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "mpdpp.h"
|
||||
#include "ncmpcpp.h"
|
||||
#include "settings.h"
|
||||
#include "status.h"
|
||||
|
||||
bool ConnectToMPD();
|
||||
void ParseArgv(int, char **);
|
||||
|
||||
@@ -58,6 +58,7 @@ void MediaLibrary::Init()
|
||||
Albums->HighlightColor(Config.main_highlight_color);
|
||||
Albums->SetTimeout(ncmpcpp_window_timeout);
|
||||
Albums->SetItemDisplayer(Display::StringPairs);
|
||||
Albums->SetGetStringFunction(StringPairToString);
|
||||
|
||||
Songs = new Menu<Song>(itsRightColStartX, main_start_y, itsRightColWidth, main_height, "Songs", Config.main_color, brNone);
|
||||
Songs->HighlightColor(Config.main_highlight_color);
|
||||
@@ -66,6 +67,7 @@ void MediaLibrary::Init()
|
||||
Songs->SetSelectSuffix(&Config.selected_item_suffix);
|
||||
Songs->SetItemDisplayer(Display::Songs);
|
||||
Songs->SetItemDisplayerUserData(&Config.song_library_format);
|
||||
Songs->SetGetStringFunction(SongToString);
|
||||
|
||||
w = Artists;
|
||||
}
|
||||
@@ -428,6 +430,16 @@ void MediaLibrary::AddToPlaylist(bool add_n_play)
|
||||
}
|
||||
}
|
||||
|
||||
std::string MediaLibrary::StringPairToString(const string_pair &pair, void *)
|
||||
{
|
||||
return pair.first;
|
||||
}
|
||||
|
||||
std::string MediaLibrary::SongToString(const MPD::Song &s, void *)
|
||||
{
|
||||
return s.toString(Config.song_library_format);
|
||||
}
|
||||
|
||||
bool MediaLibrary::SortSongsByTrack(Song *a, Song *b)
|
||||
{
|
||||
if (a->GetDisc() == b->GetDisc())
|
||||
|
||||
@@ -45,11 +45,15 @@ class MediaLibrary : public Screen<Window>
|
||||
virtual void ReverseSelection() { Songs->ReverseSelection(); }
|
||||
virtual void GetSelectedSongs(MPD::SongList &);
|
||||
|
||||
virtual void ApplyFilter(const std::string &s) { GetList()->ApplyFilter(s); }
|
||||
|
||||
virtual List *GetList();
|
||||
|
||||
void NextColumn();
|
||||
void PrevColumn();
|
||||
|
||||
static std::string StringPairToString(const string_pair &pair, void *);
|
||||
|
||||
Menu<std::string> *Artists;
|
||||
Menu<string_pair> *Albums;
|
||||
Menu<MPD::Song> *Songs;
|
||||
@@ -57,6 +61,8 @@ class MediaLibrary : public Screen<Window>
|
||||
protected:
|
||||
void AddToPlaylist(bool);
|
||||
|
||||
static std::string SongToString(const MPD::Song &s, void *);
|
||||
|
||||
static bool SortSongsByTrack(MPD::Song *, MPD::Song *);
|
||||
|
||||
static size_t itsLeftColWidth;
|
||||
|
||||
@@ -43,3 +43,7 @@ bool List::Deselect()
|
||||
return true;
|
||||
}
|
||||
|
||||
template <> std::string Menu<std::string>::GetOption(size_t pos)
|
||||
{
|
||||
return itsOptionsPtr->at(pos) ? (*itsOptionsPtr)[pos]->Item : "";
|
||||
}
|
||||
|
||||
215
src/menu.h
215
src/menu.h
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "window.h"
|
||||
#include "strbuffer.h"
|
||||
#include "misc.h"
|
||||
|
||||
class List
|
||||
{
|
||||
@@ -48,11 +49,21 @@ class List
|
||||
void SelectCurrent();
|
||||
void ReverseSelection(size_t = 0);
|
||||
bool Deselect();
|
||||
|
||||
virtual void ApplyFilter(const std::string &, size_t = 0, bool = 0) = 0;
|
||||
virtual const std::string &GetFilter() = 0;
|
||||
virtual std::string GetOption(size_t) = 0;
|
||||
|
||||
virtual bool isFiltered() = 0;
|
||||
//virtual void ShowAll() = 0;
|
||||
//virtual void ShowFiltered() = 0;
|
||||
|
||||
};
|
||||
|
||||
template <class T> class Menu : public Window, public List
|
||||
{
|
||||
typedef void (*ItemDisplayer) (const T &, void *, Menu<T> *);
|
||||
typedef std::string (*GetStringFunction) (const T &, void *);
|
||||
|
||||
struct Option
|
||||
{
|
||||
@@ -77,6 +88,9 @@ template <class T> class Menu : public Window, public List
|
||||
void SetItemDisplayer(ItemDisplayer ptr) { itsItemDisplayer = ptr; }
|
||||
void SetItemDisplayerUserData(void *data) { itsItemDisplayerUserdata = data; }
|
||||
|
||||
void SetGetStringFunction(GetStringFunction f) { itsGetStringFunction = f; }
|
||||
void SetGetStringFunctionUserData(void *data) { itsGetStringFunctionUserData = data; }
|
||||
|
||||
void Reserve(size_t size);
|
||||
void ResizeBuffer(size_t size);
|
||||
void AddOption(const T &item, bool is_bold = 0, bool is_static = 0);
|
||||
@@ -101,6 +115,15 @@ template <class T> class Menu : public Window, public List
|
||||
virtual size_t Choice() const;
|
||||
virtual size_t RealChoice() const;
|
||||
|
||||
virtual void ApplyFilter(const std::string &filter, size_t beginning = 0, bool case_sensitive = 0);
|
||||
virtual const std::string &GetFilter();
|
||||
virtual std::string GetOption(size_t pos);
|
||||
|
||||
virtual bool isFiltered() { return itsOptionsPtr == &itsFilteredOptions; }
|
||||
|
||||
void ShowAll() { itsOptionsPtr = &itsOptions; }
|
||||
void ShowFiltered() { itsOptionsPtr = &itsFilteredOptions; }
|
||||
|
||||
virtual void Refresh();
|
||||
virtual void Scroll(Where);
|
||||
virtual void Reset();
|
||||
@@ -112,7 +135,7 @@ template <class T> class Menu : public Window, public List
|
||||
void HighlightColor(Color col) { itsHighlightColor = col; }
|
||||
void Highlighting(bool hl) { highlightEnabled = hl; }
|
||||
|
||||
virtual bool Empty() const { return itsOptions.empty(); }
|
||||
virtual bool Empty() const { return itsOptionsPtr->empty(); }
|
||||
|
||||
T &Back();
|
||||
const T &Back() const;
|
||||
@@ -129,8 +152,15 @@ template <class T> class Menu : public Window, public List
|
||||
protected:
|
||||
ItemDisplayer itsItemDisplayer;
|
||||
void *itsItemDisplayerUserdata;
|
||||
GetStringFunction itsGetStringFunction;
|
||||
void *itsGetStringFunctionUserData;
|
||||
|
||||
std::string itsFilter;
|
||||
|
||||
std::vector<Option *> *itsOptionsPtr;
|
||||
std::vector<Option *> itsOptions;
|
||||
std::vector<Option *> itsFilteredOptions;
|
||||
std::vector<size_t> itsFilteredRealPositions;
|
||||
|
||||
int itsBeginning;
|
||||
int itsHighlight;
|
||||
@@ -152,6 +182,9 @@ template <class T> Menu<T>::Menu(size_t startx,
|
||||
: Window(startx, starty, width, height, title, color, border),
|
||||
itsItemDisplayer(0),
|
||||
itsItemDisplayerUserdata(0),
|
||||
itsGetStringFunction(0),
|
||||
itsGetStringFunctionUserData(0),
|
||||
itsOptionsPtr(&itsOptions),
|
||||
itsBeginning(0),
|
||||
itsHighlight(0),
|
||||
itsHighlightColor(itsBaseColor),
|
||||
@@ -215,9 +248,21 @@ template <class T> void Menu<T>::DeleteOption(size_t pos)
|
||||
{
|
||||
if (itsOptions.empty())
|
||||
return;
|
||||
delete itsOptions.at(pos);
|
||||
itsOptions.erase(itsOptions.begin()+pos);
|
||||
if (itsOptions.empty())
|
||||
if (itsOptionsPtr == &itsFilteredOptions)
|
||||
{
|
||||
delete itsOptions.at(itsFilteredRealPositions[pos]);
|
||||
itsOptions.erase(itsOptions.begin()+itsFilteredRealPositions[pos]);
|
||||
itsFilteredOptions.erase(itsFilteredOptions.begin()+pos);
|
||||
itsFilteredRealPositions.erase(itsFilteredRealPositions.begin()+pos);
|
||||
for (size_t i = pos; i < itsFilteredRealPositions.size(); i++)
|
||||
itsFilteredRealPositions[i]--;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete itsOptions.at(pos);
|
||||
itsOptions.erase(itsOptions.begin()+pos);
|
||||
}
|
||||
if (itsOptionsPtr->empty())
|
||||
Window::Clear();
|
||||
}
|
||||
|
||||
@@ -242,36 +287,42 @@ void Menu<T>::Swap(size_t one, size_t two)
|
||||
|
||||
template <class T> void Menu<T>::Refresh()
|
||||
{
|
||||
if (itsOptions.empty())
|
||||
if (itsOptionsPtr->empty())
|
||||
{
|
||||
Window::Refresh();
|
||||
return;
|
||||
}
|
||||
int MaxBeginning = itsOptions.size() < itsHeight ? 0 : itsOptions.size()-itsHeight;
|
||||
int MaxBeginning = itsOptionsPtr->size() < itsHeight ? 0 : itsOptionsPtr->size()-itsHeight;
|
||||
if (itsHighlight > itsBeginning+int(itsHeight)-1)
|
||||
itsBeginning = itsHighlight-itsHeight+1;
|
||||
if (itsBeginning < 0)
|
||||
itsBeginning = 0;
|
||||
else if (itsBeginning > MaxBeginning)
|
||||
itsBeginning = MaxBeginning;
|
||||
if (!itsOptions.empty() && itsHighlight > int(itsOptions.size())-1)
|
||||
itsHighlight = itsOptions.size()-1;
|
||||
if (!itsOptionsPtr->empty() && itsHighlight > int(itsOptionsPtr->size())-1)
|
||||
itsHighlight = itsOptionsPtr->size()-1;
|
||||
if (!(*itsOptionsPtr)[itsHighlight]) // it shouldn't be on separator.
|
||||
{
|
||||
Scroll(wUp);
|
||||
if (!(*itsOptionsPtr)[itsHighlight]) // if it's still on separator, move in other direction.
|
||||
Scroll(wDown);
|
||||
}
|
||||
size_t line = 0;
|
||||
for (size_t i = itsBeginning; i < itsBeginning+itsHeight; i++)
|
||||
{
|
||||
GotoXY(0, line);
|
||||
if (i >= itsOptions.size())
|
||||
if (i >= itsOptionsPtr->size())
|
||||
{
|
||||
for (; line < itsHeight; line++)
|
||||
mvwhline(itsWindow, line, 0, 32, itsWidth);
|
||||
break;
|
||||
}
|
||||
if (!itsOptions[i]) // separator
|
||||
if (!(*itsOptionsPtr)[i]) // separator
|
||||
{
|
||||
mvwhline(itsWindow, line++, 0, 0, itsWidth);
|
||||
continue;
|
||||
}
|
||||
if (itsOptions[i]->isBold)
|
||||
if ((*itsOptionsPtr)[i]->isBold)
|
||||
Bold(1);
|
||||
if (highlightEnabled && int(i) == itsHighlight)
|
||||
{
|
||||
@@ -279,18 +330,18 @@ template <class T> void Menu<T>::Refresh()
|
||||
*this << itsHighlightColor;
|
||||
}
|
||||
mvwhline(itsWindow, line, 0, 32, itsWidth);
|
||||
if (itsOptions[i]->isSelected && itsSelectedPrefix)
|
||||
if ((*itsOptionsPtr)[i]->isSelected && itsSelectedPrefix)
|
||||
*this << *itsSelectedPrefix;
|
||||
if (itsItemDisplayer)
|
||||
itsItemDisplayer(itsOptions[i]->Item, itsItemDisplayerUserdata, this);
|
||||
if (itsOptions[i]->isSelected && itsSelectedSuffix)
|
||||
itsItemDisplayer((*itsOptionsPtr)[i]->Item, itsItemDisplayerUserdata, this);
|
||||
if ((*itsOptionsPtr)[i]->isSelected && itsSelectedSuffix)
|
||||
*this << *itsSelectedSuffix;
|
||||
if (highlightEnabled && int(i) == itsHighlight)
|
||||
{
|
||||
*this << clEnd;
|
||||
Reverse(0);
|
||||
}
|
||||
if (itsOptions[i]->isBold)
|
||||
if ((*itsOptionsPtr)[i]->isBold)
|
||||
Bold(0);
|
||||
line++;
|
||||
}
|
||||
@@ -299,10 +350,10 @@ template <class T> void Menu<T>::Refresh()
|
||||
|
||||
template <class T> void Menu<T>::Scroll(Where where)
|
||||
{
|
||||
if (itsOptions.empty())
|
||||
if (itsOptionsPtr->empty())
|
||||
return;
|
||||
int MaxHighlight = itsOptions.size()-1;
|
||||
int MaxBeginning = itsOptions.size() < itsHeight ? 0 : itsOptions.size()-itsHeight;
|
||||
int MaxHighlight = itsOptionsPtr->size()-1;
|
||||
int MaxBeginning = itsOptionsPtr->size() < itsHeight ? 0 : itsOptionsPtr->size()-itsHeight;
|
||||
int MaxCurrentHighlight = itsBeginning+itsHeight-1;
|
||||
switch (where)
|
||||
{
|
||||
@@ -321,7 +372,7 @@ template <class T> void Menu<T>::Scroll(Where where)
|
||||
{
|
||||
itsHighlight--;
|
||||
}
|
||||
if (!itsOptions[itsHighlight] || itsOptions[itsHighlight]->isStatic)
|
||||
if (!(*itsOptionsPtr)[itsHighlight] || (*itsOptionsPtr)[itsHighlight]->isStatic)
|
||||
{
|
||||
Scroll(itsHighlight == 0 ? wDown : wUp);
|
||||
}
|
||||
@@ -342,7 +393,7 @@ template <class T> void Menu<T>::Scroll(Where where)
|
||||
{
|
||||
itsHighlight++;
|
||||
}
|
||||
if (!itsOptions[itsHighlight] || itsOptions[itsHighlight]->isStatic)
|
||||
if (!(*itsOptionsPtr)[itsHighlight] || (*itsOptionsPtr)[itsHighlight]->isStatic)
|
||||
{
|
||||
Scroll(itsHighlight == MaxHighlight ? wUp : wDown);
|
||||
}
|
||||
@@ -358,7 +409,7 @@ template <class T> void Menu<T>::Scroll(Where where)
|
||||
if (itsHighlight < 0)
|
||||
itsHighlight = 0;
|
||||
}
|
||||
if (!itsOptions[itsHighlight] || itsOptions[itsHighlight]->isStatic)
|
||||
if (!(*itsOptionsPtr)[itsHighlight] || (*itsOptionsPtr)[itsHighlight]->isStatic)
|
||||
{
|
||||
Scroll(itsHighlight == 0 ? wDown: wUp);
|
||||
}
|
||||
@@ -374,7 +425,7 @@ template <class T> void Menu<T>::Scroll(Where where)
|
||||
if (itsHighlight > MaxHighlight)
|
||||
itsHighlight = MaxHighlight;
|
||||
}
|
||||
if (!itsOptions[itsHighlight] || itsOptions[itsHighlight]->isStatic)
|
||||
if (!(*itsOptionsPtr)[itsHighlight] || (*itsOptionsPtr)[itsHighlight]->isStatic)
|
||||
{
|
||||
Scroll(itsHighlight == MaxHighlight ? wUp : wDown);
|
||||
}
|
||||
@@ -384,7 +435,7 @@ template <class T> void Menu<T>::Scroll(Where where)
|
||||
{
|
||||
itsHighlight = 0;
|
||||
itsBeginning = 0;
|
||||
if (!itsOptions[itsHighlight] || itsOptions[itsHighlight]->isStatic)
|
||||
if (!(*itsOptionsPtr)[itsHighlight] || (*itsOptionsPtr)[itsHighlight]->isStatic)
|
||||
{
|
||||
Scroll(itsHighlight == 0 ? wDown : wUp);
|
||||
}
|
||||
@@ -394,7 +445,7 @@ template <class T> void Menu<T>::Scroll(Where where)
|
||||
{
|
||||
itsHighlight = MaxHighlight;
|
||||
itsBeginning = MaxBeginning;
|
||||
if (!itsOptions[itsHighlight] || itsOptions[itsHighlight]->isStatic)
|
||||
if (!(*itsOptionsPtr)[itsHighlight] || (*itsOptionsPtr)[itsHighlight]->isStatic)
|
||||
{
|
||||
Scroll(itsHighlight == MaxHighlight ? wUp : wDown);
|
||||
}
|
||||
@@ -414,46 +465,55 @@ template <class T> void Menu<T>::Clear(bool clrscr)
|
||||
for (option_iterator it = itsOptions.begin(); it != itsOptions.end(); it++)
|
||||
delete *it;
|
||||
itsOptions.clear();
|
||||
itsFilteredOptions.clear();
|
||||
itsFilteredRealPositions.clear();
|
||||
itsFilter.clear();
|
||||
itsOptionsPtr = &itsOptions;
|
||||
if (clrscr)
|
||||
Window::Clear();
|
||||
}
|
||||
|
||||
template <class T> bool Menu<T>::isBold(int id)
|
||||
{
|
||||
return itsOptions.at(id == -1 ? itsHighlight : id)->isBold;
|
||||
id = id == -1 ? itsHighlight : id;
|
||||
if (!itsOptionsPtr->at(id))
|
||||
return 0;
|
||||
return (*itsOptionsPtr)[id]->isBold;
|
||||
}
|
||||
|
||||
template <class T> void Menu<T>::Select(int id, bool value)
|
||||
{
|
||||
if (!itsOptions.at(id))
|
||||
if (!itsOptionsPtr->at(id))
|
||||
return;
|
||||
itsOptions[id]->isSelected = value;
|
||||
(*itsOptionsPtr)[id]->isSelected = value;
|
||||
}
|
||||
|
||||
template <class T> void Menu<T>::Static(int id, bool value)
|
||||
{
|
||||
if (!itsOptions.at(id))
|
||||
if (!itsOptionsPtr->at(id))
|
||||
return;
|
||||
itsOptions[id]->isStatic = value;
|
||||
(*itsOptionsPtr)[id]->isStatic = value;
|
||||
}
|
||||
|
||||
template <class T> bool Menu<T>::isSelected(int id) const
|
||||
{
|
||||
if (!itsOptions.at(id == -1 ? itsHighlight : id))
|
||||
id = id == -1 ? itsHighlight : id;
|
||||
if (!itsOptionsPtr->at(id))
|
||||
return 0;
|
||||
return itsOptions[id == -1 ? itsHighlight : id]->isSelected;
|
||||
return (*itsOptionsPtr)[id]->isSelected;
|
||||
}
|
||||
|
||||
template <class T> bool Menu<T>::isStatic(int id) const
|
||||
{
|
||||
if (!itsOptions.at(id == -1 ? itsHighlight : id))
|
||||
id = id == -1 ? itsHighlight : id;
|
||||
if (!itsOptionsPtr->at(id))
|
||||
return 1;
|
||||
return itsOptions[id == -1 ? itsHighlight : id]->isStatic;
|
||||
return (*itsOptionsPtr)[id]->isStatic;
|
||||
}
|
||||
|
||||
template <class T> bool Menu<T>::hasSelected() const
|
||||
{
|
||||
for (option_const_iterator it = itsOptions.begin(); it != itsOptions.end(); it++)
|
||||
for (option_const_iterator it = itsOptionsPtr->begin(); it != itsOptionsPtr->end(); it++)
|
||||
if (*it && (*it)->isSelected)
|
||||
return true;
|
||||
return false;
|
||||
@@ -461,8 +521,8 @@ template <class T> bool Menu<T>::hasSelected() const
|
||||
|
||||
template <class T> void Menu<T>::GetSelected(std::vector<size_t> &v) const
|
||||
{
|
||||
for (size_t i = 0; i < itsOptions.size(); i++)
|
||||
if (itsOptions[i]->isSelected)
|
||||
for (size_t i = 0; i < itsOptionsPtr->size(); i++)
|
||||
if ((*itsOptionsPtr)[i]->isSelected)
|
||||
v.push_back(i);
|
||||
}
|
||||
|
||||
@@ -474,7 +534,7 @@ template <class T> void Menu<T>::Highlight(size_t pos)
|
||||
|
||||
template <class T> size_t Menu<T>::Size() const
|
||||
{
|
||||
return itsOptions.size();
|
||||
return itsOptionsPtr->size();
|
||||
}
|
||||
|
||||
template <class T> size_t Menu<T>::Choice() const
|
||||
@@ -485,66 +545,113 @@ template <class T> size_t Menu<T>::Choice() const
|
||||
template <class T> size_t Menu<T>::RealChoice() const
|
||||
{
|
||||
size_t result = 0;
|
||||
for (option_const_iterator it = itsOptions.begin(); it != itsOptions.begin()+itsHighlight; it++)
|
||||
for (option_const_iterator it = itsOptionsPtr->begin(); it != itsOptionsPtr->begin()+itsHighlight; it++)
|
||||
if (*it && !(*it)->isStatic)
|
||||
result++;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T> void Menu<T>::ApplyFilter(const std::string &filter, size_t beginning, bool case_sensitive)
|
||||
{
|
||||
itsFilter = filter;
|
||||
if (!case_sensitive)
|
||||
ToLower(itsFilter);
|
||||
itsFilteredRealPositions.clear();
|
||||
itsFilteredOptions.clear();
|
||||
itsOptionsPtr = &itsOptions;
|
||||
if (itsFilter.empty())
|
||||
return;
|
||||
for (size_t i = 0; i < beginning; i++)
|
||||
{
|
||||
itsFilteredRealPositions.push_back(i);
|
||||
itsFilteredOptions.push_back(itsOptions[i]);
|
||||
}
|
||||
std::string option;
|
||||
for (size_t i = beginning; i < itsOptions.size(); i++)
|
||||
{
|
||||
option = GetOption(i);
|
||||
if (!case_sensitive)
|
||||
ToLower(option);
|
||||
if (option.find(itsFilter) != std::string::npos)
|
||||
{
|
||||
itsFilteredRealPositions.push_back(i);
|
||||
itsFilteredOptions.push_back(itsOptions[i]);
|
||||
}
|
||||
}
|
||||
itsOptionsPtr = &itsFilteredOptions;
|
||||
if (itsOptionsPtr->empty()) // oops, we didn't find anything
|
||||
Window::Clear();
|
||||
}
|
||||
|
||||
template <class T> const std::string &Menu<T>::GetFilter()
|
||||
{
|
||||
return itsFilter;
|
||||
}
|
||||
|
||||
template <class T> std::string Menu<T>::GetOption(size_t pos)
|
||||
{
|
||||
if (itsOptionsPtr->at(pos) && itsGetStringFunction)
|
||||
return itsGetStringFunction((*itsOptionsPtr)[pos]->Item, itsGetStringFunctionUserData);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
template <> std::string Menu<std::string>::GetOption(size_t pos);
|
||||
|
||||
template <class T> T &Menu<T>::Back()
|
||||
{
|
||||
if (!itsOptions.back())
|
||||
if (!itsOptionsPtr->back())
|
||||
throw InvalidItem();
|
||||
return itsOptions.back()->Item;
|
||||
return itsOptionsPtr->back()->Item;
|
||||
}
|
||||
|
||||
template <class T> const T &Menu<T>::Back() const
|
||||
{
|
||||
if (!itsOptions.back())
|
||||
if (!itsOptionsPtr->back())
|
||||
throw InvalidItem();
|
||||
return itsOptions.back()->Item;
|
||||
return itsOptionsPtr->back()->Item;
|
||||
}
|
||||
|
||||
template <class T> T &Menu<T>::Current()
|
||||
{
|
||||
if (!itsOptions.at(itsHighlight))
|
||||
if (!itsOptionsPtr->at(itsHighlight))
|
||||
throw InvalidItem();
|
||||
return itsOptions[itsHighlight]->Item;
|
||||
return (*itsOptionsPtr)[itsHighlight]->Item;
|
||||
}
|
||||
|
||||
template <class T> const T &Menu<T>::Current() const
|
||||
{
|
||||
if (!itsOptions.at(itsHighlight))
|
||||
if (!itsOptionsPtr->at(itsHighlight))
|
||||
throw InvalidItem();
|
||||
return itsOptions[itsHighlight]->Item;
|
||||
return (*itsOptionsPtr)[itsHighlight]->Item;
|
||||
}
|
||||
|
||||
template <class T> T &Menu<T>::at(size_t i)
|
||||
{
|
||||
if (!itsOptions.at(i))
|
||||
if (!itsOptionsPtr->at(i))
|
||||
throw InvalidItem();
|
||||
return itsOptions[i]->Item;
|
||||
return (*itsOptionsPtr)[i]->Item;
|
||||
}
|
||||
|
||||
template <class T> const T &Menu<T>::at(size_t i) const
|
||||
{
|
||||
if (!itsOptions.at(i))
|
||||
if (!itsOptions->at(i))
|
||||
throw InvalidItem();
|
||||
return itsOptions.at(i)->Item;
|
||||
return (*itsOptionsPtr)[i]->Item;
|
||||
}
|
||||
|
||||
template <class T> const T &Menu<T>::operator[](size_t i) const
|
||||
{
|
||||
if (!itsOptions[i])
|
||||
if (!(*itsOptionsPtr)[i])
|
||||
throw InvalidItem();
|
||||
return itsOptions[i]->Item;
|
||||
return (*itsOptionsPtr)[i]->Item;
|
||||
}
|
||||
|
||||
template <class T> T &Menu<T>::operator[](size_t i)
|
||||
{
|
||||
if (!itsOptions[i])
|
||||
if (!(*itsOptionsPtr)[i])
|
||||
throw InvalidItem();
|
||||
return itsOptions[i]->Item;
|
||||
return (*itsOptionsPtr)[i]->Item;
|
||||
}
|
||||
|
||||
template <class T> Menu<T> *Menu<T>::EmptyClone() const
|
||||
|
||||
@@ -27,6 +27,7 @@ using std::string;
|
||||
|
||||
const char *MPD::Message::PartOfSongsAdded = "Only part of requested songs' list added to playlist!";
|
||||
const char *MPD::Message::FullPlaylist = "Playlist is full!";
|
||||
const char *MPD::Message::FunctionDisabledFilteringEnabled = "Function disabled due to enabled filtering in playlist";
|
||||
|
||||
Connection::Connection() : isConnected(0),
|
||||
itsErrorCode(0),
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace MPD
|
||||
{
|
||||
extern const char *PartOfSongsAdded;
|
||||
extern const char *FullPlaylist;
|
||||
extern const char *FunctionDisabledFilteringEnabled;
|
||||
}
|
||||
|
||||
enum QueueCommandType { qctAdd, qctAddToPlaylist, qctDelete, qctDeleteID, qctMove, qctPlaylistMove, qctDeleteFromPlaylist };
|
||||
|
||||
210
src/ncmpcpp.cpp
210
src/ncmpcpp.cpp
@@ -161,7 +161,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
wFooter = new Window(0, footer_start_y, COLS, footer_height, "", Config.statusbar_color, brNone);
|
||||
wFooter->SetTimeout(ncmpcpp_window_timeout);
|
||||
wFooter->SetGetStringHelper(TraceMpdStatus);
|
||||
wFooter->SetGetStringHelper(StatusbarGetStringHelper);
|
||||
wFooter->Display();
|
||||
|
||||
myScreen = myPlaylist;
|
||||
@@ -180,6 +180,7 @@ int main(int argc, char *argv[])
|
||||
string screen_title;
|
||||
|
||||
timeval now, past;
|
||||
|
||||
// local variables end
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
@@ -266,7 +267,17 @@ int main(int argc, char *argv[])
|
||||
# endif // HAVE_TAGLIB_H
|
||||
)
|
||||
{
|
||||
if (Keypressed(input, Key.Up) || Keypressed(input, Key.Down) || Keypressed(input, Key.PageUp) || Keypressed(input, Key.PageDown) || Keypressed(input, Key.Home) || Keypressed(input, Key.End) || Keypressed(input, Key.FindForward) || Keypressed(input, Key.FindBackward) || Keypressed(input, Key.NextFoundPosition) || Keypressed(input, Key.PrevFoundPosition))
|
||||
if (Keypressed(input, Key.Up)
|
||||
|| Keypressed(input, Key.Down)
|
||||
|| Keypressed(input, Key.PageUp)
|
||||
|| Keypressed(input, Key.PageDown)
|
||||
|| Keypressed(input, Key.Home)
|
||||
|| Keypressed(input, Key.End)
|
||||
|| Keypressed(input, Key.ApplyFilter)
|
||||
|| Keypressed(input, Key.FindForward)
|
||||
|| Keypressed(input, Key.FindBackward)
|
||||
|| Keypressed(input, Key.NextFoundPosition)
|
||||
|| Keypressed(input, Key.PrevFoundPosition))
|
||||
{
|
||||
if (myScreen->Cmp() == myLibrary->Artists)
|
||||
{
|
||||
@@ -438,7 +449,7 @@ int main(int argc, char *argv[])
|
||||
myPlaylist->Main()->GetSelected(list);
|
||||
for (vector<size_t>::const_reverse_iterator it = list.rbegin(); it != ((const vector<size_t> &)list).rend(); it++)
|
||||
{
|
||||
Mpd->QueueDeleteSong(*it);
|
||||
Mpd->QueueDeleteSongId((*myPlaylist->Main())[*it].GetID());
|
||||
myPlaylist->Main()->DeleteOption(*it);
|
||||
}
|
||||
ShowMessage("Selected items deleted!");
|
||||
@@ -452,9 +463,9 @@ int main(int argc, char *argv[])
|
||||
size_t id = myPlaylist->Main()->Choice();
|
||||
TraceMpdStatus();
|
||||
timer = time(NULL);
|
||||
if (size_t(myPlaylist->NowPlaying) > id) // needed for keeping proper
|
||||
if (myPlaylist->NowPlaying > myPlaylist->CurrentSong()->GetPosition()) // needed for keeping proper
|
||||
myPlaylist->NowPlaying--; // position of now playing song.
|
||||
Mpd->QueueDeleteSong(id);
|
||||
Mpd->QueueDeleteSongId(myPlaylist->CurrentSong()->GetID());
|
||||
myPlaylist->Main()->DeleteOption(id);
|
||||
myPlaylist->Main()->Refresh();
|
||||
myPlaylist->Main()->ReadKey(input);
|
||||
@@ -597,6 +608,12 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
if (myScreen == myPlaylist && !myPlaylist->Main()->Empty())
|
||||
{
|
||||
if (myPlaylist->Main()->isFiltered())
|
||||
{
|
||||
ShowMessage("%s", MPD::Message::FunctionDisabledFilteringEnabled);
|
||||
continue;
|
||||
}
|
||||
|
||||
Playlist::BlockUpdate = 1;
|
||||
myPlaylist->Main()->SetTimeout(50);
|
||||
if (myPlaylist->Main()->hasSelected())
|
||||
@@ -700,6 +717,12 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
if (myScreen == myPlaylist && !myPlaylist->Main()->Empty())
|
||||
{
|
||||
if (myPlaylist->Main()->isFiltered())
|
||||
{
|
||||
ShowMessage("%s", MPD::Message::FunctionDisabledFilteringEnabled);
|
||||
continue;
|
||||
}
|
||||
|
||||
Playlist::BlockUpdate = 1;
|
||||
myPlaylist->Main()->SetTimeout(50);
|
||||
if (myPlaylist->Main()->hasSelected())
|
||||
@@ -830,7 +853,10 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
if (!myPlaylist->isPlaying())
|
||||
continue;
|
||||
if (!myPlaylist->NowPlayingSong().GetTotalLength())
|
||||
|
||||
const Song &s = Mpd->GetCurrentSong();
|
||||
|
||||
if (!s.GetTotalLength())
|
||||
{
|
||||
ShowMessage("Unknown item length!");
|
||||
continue;
|
||||
@@ -842,7 +868,6 @@ int main(int argc, char *argv[])
|
||||
time_t t = time(NULL);
|
||||
|
||||
songpos = Mpd->GetElapsedTime();
|
||||
const Song &s = myPlaylist->NowPlayingSong();
|
||||
|
||||
while (Keypressed(input, Key.SeekForward) || Keypressed(input, Key.SeekBackward))
|
||||
{
|
||||
@@ -888,15 +913,30 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
Config.columns_in_playlist = !Config.columns_in_playlist;
|
||||
ShowMessage("Playlist display mode: %s", Config.columns_in_playlist ? "Columns" : "Classic");
|
||||
myPlaylist->Main()->SetItemDisplayer(Config.columns_in_playlist ? Display::SongsInColumns : Display::Songs);
|
||||
myPlaylist->Main()->SetItemDisplayerUserData(Config.columns_in_playlist ? &Config.song_columns_list_format : &Config.song_list_format);
|
||||
myPlaylist->Main()->SetTitle(Config.columns_in_playlist ? Display::Columns(Config.song_columns_list_format) : "");
|
||||
|
||||
if (Config.columns_in_playlist)
|
||||
{
|
||||
myPlaylist->Main()->SetItemDisplayer(Display::SongsInColumns);
|
||||
myPlaylist->Main()->SetItemDisplayerUserData(&Config.song_columns_list_format);
|
||||
myPlaylist->Main()->SetTitle(Display::Columns(Config.song_columns_list_format));
|
||||
myPlaylist->Main()->SetGetStringFunction(Playlist::SongInColumnsToString);
|
||||
myPlaylist->Main()->SetGetStringFunctionUserData(&Config.song_columns_list_format);
|
||||
}
|
||||
else
|
||||
{
|
||||
myPlaylist->Main()->SetItemDisplayer(Display::Songs);
|
||||
myPlaylist->Main()->SetItemDisplayerUserData(&Config.song_list_format);
|
||||
myPlaylist->Main()->SetTitle("");
|
||||
myPlaylist->Main()->SetGetStringFunction(Playlist::SongToString);
|
||||
myPlaylist->Main()->SetGetStringFunctionUserData(&Config.song_list_format);
|
||||
}
|
||||
}
|
||||
else if (myScreen == myBrowser)
|
||||
{
|
||||
Config.columns_in_browser = !Config.columns_in_browser;
|
||||
ShowMessage("Browser display mode: %s", Config.columns_in_browser ? "Columns" : "Classic");
|
||||
myBrowser->Main()->SetTitle(Config.columns_in_browser ? Display::Columns(Config.song_columns_list_format) : "");
|
||||
|
||||
}
|
||||
else if (myScreen == mySearcher)
|
||||
{
|
||||
@@ -921,7 +961,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
Config.autocenter_mode = !Config.autocenter_mode;
|
||||
ShowMessage("Auto center mode: %s", Config.autocenter_mode ? "On" : "Off");
|
||||
if (Config.autocenter_mode && myPlaylist->isPlaying())
|
||||
if (Config.autocenter_mode && myPlaylist->isPlaying() && !myPlaylist->Main()->isFiltered())
|
||||
myPlaylist->Main()->Highlight(myPlaylist->NowPlaying);
|
||||
}
|
||||
else if (Keypressed(input, Key.UpdateDB))
|
||||
@@ -937,8 +977,14 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else if (Keypressed(input, Key.GoToNowPlaying))
|
||||
{
|
||||
if (myScreen == myPlaylist && myPlaylist->isPlaying())
|
||||
myPlaylist->Main()->Highlight(myPlaylist->NowPlaying);
|
||||
if (myScreen != myPlaylist || !myPlaylist->isPlaying())
|
||||
continue;
|
||||
if (myPlaylist->Main()->isFiltered())
|
||||
{
|
||||
ShowMessage("%s", MPD::Message::FunctionDisabledFilteringEnabled);
|
||||
continue;
|
||||
}
|
||||
myPlaylist->Main()->Highlight(myPlaylist->NowPlaying);
|
||||
}
|
||||
else if (Keypressed(input, Key.ToggleRepeat))
|
||||
{
|
||||
@@ -1163,7 +1209,10 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
if (!myPlaylist->isPlaying())
|
||||
continue;
|
||||
if (!myPlaylist->NowPlayingSong().GetTotalLength())
|
||||
|
||||
const Song &s = Mpd->GetCurrentSong();
|
||||
|
||||
if (!s.GetTotalLength())
|
||||
{
|
||||
ShowMessage("Unknown item length!");
|
||||
continue;
|
||||
@@ -1173,7 +1222,7 @@ int main(int argc, char *argv[])
|
||||
string position = wFooter->GetString(3);
|
||||
int newpos = StrToInt(position);
|
||||
if (newpos > 0 && newpos < 100 && !position.empty())
|
||||
Mpd->Seek(myPlaylist->NowPlayingSong().GetTotalLength()*newpos/100.0);
|
||||
Mpd->Seek(s.GetTotalLength()*newpos/100.0);
|
||||
UnlockStatusbar();
|
||||
}
|
||||
else if (Keypressed(input, Key.ReverseSelection))
|
||||
@@ -1317,6 +1366,11 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else if (Keypressed(input, Key.Crop))
|
||||
{
|
||||
if (myPlaylist->Main()->isFiltered())
|
||||
{
|
||||
ShowMessage("%s", MPD::Message::FunctionDisabledFilteringEnabled);
|
||||
continue;
|
||||
}
|
||||
if (myPlaylist->Main()->hasSelected())
|
||||
{
|
||||
for (int i = myPlaylist->Main()->Size()-1; i >= 0; i--)
|
||||
@@ -1354,6 +1408,34 @@ int main(int argc, char *argv[])
|
||||
Mpd->ClearPlaylist();
|
||||
ShowMessage("Cleared playlist!");
|
||||
}
|
||||
else if (Keypressed(input, Key.ApplyFilter))
|
||||
{
|
||||
List *mList = myScreen->GetList();
|
||||
|
||||
if (!mList)
|
||||
continue;
|
||||
|
||||
CLEAR_FIND_HISTORY;
|
||||
|
||||
LockStatusbar();
|
||||
Statusbar() << fmtBold << "Apply filter: " << fmtBoldEnd;
|
||||
wFooter->SetGetStringHelper(StatusbarApplyFilterImmediately);
|
||||
wFooter->GetString(mList->GetFilter());
|
||||
wFooter->SetGetStringHelper(StatusbarGetStringHelper);
|
||||
UnlockStatusbar();
|
||||
|
||||
if (mList->isFiltered())
|
||||
ShowMessage("Using filter '%s'", mList->GetFilter().c_str());
|
||||
else
|
||||
ShowMessage("Filtering disabled");
|
||||
|
||||
if (myScreen == myPlaylist)
|
||||
{
|
||||
timer = time(NULL);
|
||||
myPlaylist->Main()->Highlighting(1);
|
||||
redraw_header = 1;
|
||||
}
|
||||
}
|
||||
else if (Keypressed(input, Key.FindForward) || Keypressed(input, Key.FindBackward))
|
||||
{
|
||||
List *mList = myScreen->GetList();
|
||||
@@ -1376,103 +1458,7 @@ int main(int argc, char *argv[])
|
||||
ShowMessage("Searching...");
|
||||
for (size_t i = (myScreen == mySearcher ? SearchEngine::StaticOptions : 0); i < mList->Size(); i++)
|
||||
{
|
||||
string name;
|
||||
if (myScreen == myPlaylist)
|
||||
{
|
||||
name = myPlaylist->Main()->at(i).toString(Config.song_list_format);
|
||||
}
|
||||
else if (myScreen == myBrowser)
|
||||
{
|
||||
switch (myBrowser->Main()->at(i).type)
|
||||
{
|
||||
case itDirectory:
|
||||
name = ExtractTopDirectory(myBrowser->Main()->at(i).name);
|
||||
break;
|
||||
case itSong:
|
||||
name = myBrowser->Main()->at(i).song->toString(Config.song_list_format);
|
||||
break;
|
||||
case itPlaylist:
|
||||
name = Config.browser_playlist_prefix.Str();
|
||||
name += myBrowser->Main()->at(i).name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (myScreen == mySearcher)
|
||||
{
|
||||
name = mySearcher->Main()->at(i).second->toString(Config.song_list_format);
|
||||
}
|
||||
else if (myScreen == myLibrary)
|
||||
{
|
||||
if (myScreen->Cmp() == myLibrary->Artists)
|
||||
name = myLibrary->Artists->at(i);
|
||||
else if (myScreen->Cmp() == myLibrary->Albums)
|
||||
name = myLibrary->Albums->at(i).first;
|
||||
else
|
||||
name = myLibrary->Songs->at(i).toString(Config.song_library_format);
|
||||
}
|
||||
else if (myScreen == myPlaylistEditor)
|
||||
{
|
||||
if (myScreen->Cmp() == myPlaylistEditor->Playlists)
|
||||
name = myPlaylistEditor->Playlists->at(i);
|
||||
else
|
||||
name = myPlaylistEditor->Content->at(i).toString(Config.song_list_format);
|
||||
}
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
else if (myScreen == myTagEditor)
|
||||
{
|
||||
if (myScreen->Cmp() == myTagEditor->LeftColumn)
|
||||
name = myTagEditor->LeftColumn->at(i).first;
|
||||
else
|
||||
{
|
||||
const Song &s = myTagEditor->Tags->at(i);
|
||||
switch (myTagEditor->TagTypes->Choice())
|
||||
{
|
||||
case 0:
|
||||
name = s.GetTitle();
|
||||
break;
|
||||
case 1:
|
||||
name = s.GetArtist();
|
||||
break;
|
||||
case 2:
|
||||
name = s.GetAlbum();
|
||||
break;
|
||||
case 3:
|
||||
name = s.GetYear();
|
||||
break;
|
||||
case 4:
|
||||
name = s.GetTrack();
|
||||
break;
|
||||
case 5:
|
||||
name = s.GetGenre();
|
||||
break;
|
||||
case 6:
|
||||
name = s.GetComposer();
|
||||
break;
|
||||
case 7:
|
||||
name = s.GetPerformer();
|
||||
break;
|
||||
case 8:
|
||||
name = s.GetDisc();
|
||||
break;
|
||||
case 9:
|
||||
name = s.GetComment();
|
||||
break;
|
||||
case 11:
|
||||
if (s.GetNewName().empty())
|
||||
name = s.GetName();
|
||||
else
|
||||
{
|
||||
name = s.GetName();
|
||||
name += " -> ";
|
||||
name += s.GetNewName();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
# endif // HAVE_TAGLIB_H
|
||||
string name = mList->GetOption(i);
|
||||
ToLower(name);
|
||||
if (name.find(findme) != string::npos && !mList->isStatic(i))
|
||||
{
|
||||
|
||||
@@ -44,6 +44,8 @@ void Playlist::Init()
|
||||
w->SetSelectSuffix(&Config.selected_item_suffix);
|
||||
w->SetItemDisplayer(Config.columns_in_playlist ? Display::SongsInColumns : Display::Songs);
|
||||
w->SetItemDisplayerUserData(Config.columns_in_playlist ? &Config.song_columns_list_format : &Config.song_list_format);
|
||||
w->SetGetStringFunction(Config.columns_in_playlist ? SongInColumnsToString : SongToString);
|
||||
w->SetGetStringFunctionUserData(Config.columns_in_playlist ? &Config.song_columns_list_format : &Config.song_list_format);
|
||||
}
|
||||
|
||||
void Playlist::SwitchTo()
|
||||
@@ -56,6 +58,7 @@ void Playlist::SwitchTo()
|
||||
|
||||
CLEAR_FIND_HISTORY;
|
||||
myScreen = this;
|
||||
w->Window::Clear();
|
||||
redraw_header = 1;
|
||||
}
|
||||
|
||||
@@ -115,6 +118,15 @@ std::string Playlist::TotalLength()
|
||||
|
||||
result << '(' << w->Size() << (w->Size() == 1 ? " item" : " items");
|
||||
|
||||
if (w->isFiltered())
|
||||
{
|
||||
w->ShowAll();
|
||||
size_t real_size = w->Size();
|
||||
w->ShowFiltered();
|
||||
if (w->Size() != real_size)
|
||||
result << " (out of " << Mpd->GetPlaylistLength() << ")";
|
||||
}
|
||||
|
||||
if (length)
|
||||
{
|
||||
result << ", length: ";
|
||||
@@ -162,3 +174,22 @@ const MPD::Song &Playlist::NowPlayingSong()
|
||||
static MPD::Song null;
|
||||
return isPlaying() ? w->at(NowPlaying) : null;
|
||||
}
|
||||
|
||||
std::string Playlist::SongToString(const MPD::Song &s, void *data)
|
||||
{
|
||||
return s.toString(*static_cast<std::string *>(data));
|
||||
}
|
||||
|
||||
std::string Playlist::SongInColumnsToString(const MPD::Song &s, void *data)
|
||||
{
|
||||
std::string result;
|
||||
std::string fmt = *static_cast<std::string *>(data);
|
||||
for (std::string i = GetLineValue(fmt, '{', '}', 1); !i.empty(); i = GetLineValue(fmt, '{', '}', 1))
|
||||
{
|
||||
result += "%";
|
||||
result += i;
|
||||
result += " ";
|
||||
}
|
||||
return s.toString(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,11 +46,16 @@ class Playlist : public Screen< Menu<MPD::Song> >
|
||||
virtual void ReverseSelection() { w->ReverseSelection(); }
|
||||
virtual void GetSelectedSongs(MPD::SongList &);
|
||||
|
||||
virtual void ApplyFilter(const std::string &s) { w->ApplyFilter(s); }
|
||||
|
||||
virtual List *GetList() { return w; }
|
||||
|
||||
bool isPlaying() { return NowPlaying >= 0 && !w->Empty(); }
|
||||
const MPD::Song &NowPlayingSong();
|
||||
|
||||
static std::string SongToString(const MPD::Song &, void *);
|
||||
static std::string SongInColumnsToString(const MPD::Song &, void *);
|
||||
|
||||
int NowPlaying;
|
||||
int OldPlaying;
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ void PlaylistEditor::Init()
|
||||
Content->SetSelectSuffix(&Config.selected_item_suffix);
|
||||
Content->SetItemDisplayer(Display::Songs);
|
||||
Content->SetItemDisplayerUserData(&Config.song_list_format);
|
||||
Content->SetGetStringFunction(Playlist::SongToString);
|
||||
Content->SetGetStringFunctionUserData(&Config.song_list_format);
|
||||
|
||||
w = Playlists;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ class PlaylistEditor : public Screen<Window>
|
||||
virtual void ReverseSelection() { Content->ReverseSelection(); }
|
||||
virtual void GetSelectedSongs(MPD::SongList &);
|
||||
|
||||
virtual void ApplyFilter(const std::string &s) { GetList()->ApplyFilter(s); }
|
||||
|
||||
virtual List *GetList();
|
||||
|
||||
void NextColumn();
|
||||
|
||||
@@ -56,6 +56,8 @@ class BasicScreen
|
||||
virtual void ReverseSelection() { }
|
||||
virtual void GetSelectedSongs(MPD::SongList &) { }
|
||||
|
||||
virtual void ApplyFilter(const std::string &) { }
|
||||
|
||||
virtual List *GetList() = 0;
|
||||
|
||||
bool hasToBeResized;
|
||||
|
||||
@@ -50,6 +50,7 @@ void SearchEngine::Init()
|
||||
w->SetItemDisplayer(Display::SearchEngine);
|
||||
w->SetSelectPrefix(&Config.selected_item_prefix);
|
||||
w->SetSelectSuffix(&Config.selected_item_suffix);
|
||||
w->SetGetStringFunction(SearchEngineOptionToString);
|
||||
}
|
||||
|
||||
void SearchEngine::Resize()
|
||||
@@ -552,3 +553,11 @@ void SearchEngine::Search()
|
||||
FreeSongList(list);
|
||||
}
|
||||
|
||||
std::string SearchEngine::SearchEngineOptionToString(const std::pair<Buffer *, MPD::Song *> &pair, void *)
|
||||
{
|
||||
if (!Config.columns_in_search_engine)
|
||||
return pair.second->toString(Config.song_list_format);
|
||||
else
|
||||
return Playlist::SongInColumnsToString(*pair.second, &Config.song_columns_list_format);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,9 @@ class SearchEngine : public Screen< Menu< std::pair<Buffer *, MPD::Song *> > >
|
||||
virtual void ReverseSelection() { w->ReverseSelection(StaticOptions); }
|
||||
virtual void GetSelectedSongs(MPD::SongList &);
|
||||
|
||||
virtual List *GetList() { return w; }
|
||||
virtual void ApplyFilter(const std::string &s) { w->ApplyFilter(s, StaticOptions); }
|
||||
|
||||
virtual List *GetList() { return w->Size() >= StaticOptions ? w : 0; }
|
||||
|
||||
void UpdateFoundList();
|
||||
|
||||
@@ -70,6 +72,8 @@ class SearchEngine : public Screen< Menu< std::pair<Buffer *, MPD::Song *> > >
|
||||
void Prepare();
|
||||
void Search();
|
||||
|
||||
static std::string SearchEngineOptionToString(const std::pair<Buffer *, MPD::Song *> &, void *);
|
||||
|
||||
SearchPattern itsPattern;
|
||||
|
||||
static bool MatchToPattern;
|
||||
|
||||
@@ -117,6 +117,7 @@ void DefaultKeys(ncmpcpp_keys &keys)
|
||||
keys.ToggleCrossfade[0] = 'x';
|
||||
keys.SetCrossfade[0] = 'X';
|
||||
keys.UpdateDB[0] = 'u';
|
||||
keys.ApplyFilter[0] = 6;
|
||||
keys.FindForward[0] = '/';
|
||||
keys.FindBackward[0] = '?';
|
||||
keys.NextFoundPosition[0] = '.';
|
||||
@@ -181,6 +182,7 @@ void DefaultKeys(ncmpcpp_keys &keys)
|
||||
keys.ToggleCrossfade[1] = null_key;
|
||||
keys.SetCrossfade[1] = null_key;
|
||||
keys.UpdateDB[1] = null_key;
|
||||
keys.ApplyFilter[1] = null_key;
|
||||
keys.FindForward[1] = null_key;
|
||||
keys.FindBackward[1] = null_key;
|
||||
keys.NextFoundPosition[1] = null_key;
|
||||
@@ -354,6 +356,8 @@ void ReadKeys(ncmpcpp_keys &keys)
|
||||
GetKeys(key, keys.SetCrossfade);
|
||||
else if (key.find("key_update_db ") != string::npos)
|
||||
GetKeys(key, keys.UpdateDB);
|
||||
else if (key.find("key_apply_filter ") != string::npos)
|
||||
GetKeys(key, keys.ApplyFilter);
|
||||
else if (key.find("key_find_forward ") != string::npos)
|
||||
GetKeys(key, keys.FindForward);
|
||||
else if (key.find("key_find_backward ") != string::npos)
|
||||
|
||||
@@ -66,6 +66,7 @@ struct ncmpcpp_keys
|
||||
int ToggleCrossfade[2];
|
||||
int SetCrossfade[2];
|
||||
int UpdateDB[2];
|
||||
int ApplyFilter[2];
|
||||
int FindForward[2];
|
||||
int FindBackward[2];
|
||||
int NextFoundPosition[2];
|
||||
|
||||
@@ -61,6 +61,18 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
void StatusbarGetStringHelper(const std::wstring &)
|
||||
{
|
||||
TraceMpdStatus();
|
||||
}
|
||||
|
||||
void StatusbarApplyFilterImmediately(const std::wstring &ws)
|
||||
{
|
||||
myScreen->ApplyFilter(ToString(ws));
|
||||
myScreen->RefreshWindow();
|
||||
TraceMpdStatus();
|
||||
}
|
||||
|
||||
void LockStatusbar()
|
||||
{
|
||||
if (Config.statusbar_visibility)
|
||||
@@ -130,7 +142,7 @@ void NcmpcppErrorCallback(Connection *Mpd, int errorid, const char *msg, void *)
|
||||
Mpd->SetPassword(password);
|
||||
Mpd->SendPassword();
|
||||
Mpd->UpdateStatus();
|
||||
wFooter->SetGetStringHelper(TraceMpdStatus);
|
||||
wFooter->SetGetStringHelper(StatusbarGetStringHelper);
|
||||
}
|
||||
else
|
||||
ShowMessage("%s", msg);
|
||||
@@ -161,6 +173,8 @@ void NcmpcppStatusChanged(Connection *Mpd, StatusChanges changed, void *)
|
||||
{
|
||||
if (!Playlist::BlockUpdate)
|
||||
{
|
||||
bool was_filtered = myPlaylist->Main()->isFiltered();
|
||||
myPlaylist->Main()->ShowAll();
|
||||
SongList list;
|
||||
size_t playlist_length = Mpd->GetPlaylistLength();
|
||||
if (playlist_length != myPlaylist->Main()->Size())
|
||||
@@ -212,6 +226,8 @@ void NcmpcppStatusChanged(Connection *Mpd, StatusChanges changed, void *)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (was_filtered)
|
||||
myPlaylist->ApplyFilter(myPlaylist->Main()->GetFilter());
|
||||
FreeSongList(list);
|
||||
}
|
||||
|
||||
@@ -316,7 +332,7 @@ void NcmpcppStatusChanged(Connection *Mpd, StatusChanges changed, void *)
|
||||
}
|
||||
catch (std::out_of_range &) { }
|
||||
myPlaylist->Main()->BoldOption(myPlaylist->NowPlaying, 1);
|
||||
if (Config.autocenter_mode)
|
||||
if (Config.autocenter_mode && !myPlaylist->Main()->isFiltered())
|
||||
myPlaylist->Main()->Highlight(myPlaylist->NowPlaying);
|
||||
repeat_one_allowed = 0;
|
||||
|
||||
|
||||
@@ -34,5 +34,8 @@ void NcmpcppErrorCallback(MPD::Connection *, int, const char *, void *);
|
||||
Window &Statusbar();
|
||||
void ShowMessage(const char *, ...);
|
||||
|
||||
void StatusbarGetStringHelper(const std::wstring &);
|
||||
void StatusbarApplyFilterImmediately(const std::wstring &);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -304,11 +304,14 @@ void TagEditor::Init()
|
||||
Albums->HighlightColor(Config.active_column_color);
|
||||
Albums->SetTimeout(ncmpcpp_window_timeout);
|
||||
Albums->SetItemDisplayer(Display::StringPairs);
|
||||
Albums->SetGetStringFunction(MediaLibrary::StringPairToString);
|
||||
|
||||
Dirs = new Menu<string_pair>(0, main_start_y, LeftColumnWidth, main_height, "Directories", Config.main_color, brNone);
|
||||
Dirs->HighlightColor(Config.active_column_color);
|
||||
Dirs->SetTimeout(ncmpcpp_window_timeout);
|
||||
Dirs->SetItemDisplayer(Display::StringPairs);
|
||||
Dirs->SetGetStringFunction(MediaLibrary::StringPairToString);
|
||||
|
||||
LeftColumn = Config.albums_in_tag_editor ? Albums : Dirs;
|
||||
|
||||
TagTypes = new Menu<string>(MiddleColumnStartX, main_start_y, MiddleColumnWidth, main_height, "Tag types", Config.main_color, brNone);
|
||||
@@ -323,6 +326,8 @@ void TagEditor::Init()
|
||||
Tags->SetSelectSuffix(&Config.selected_item_suffix);
|
||||
Tags->SetItemDisplayer(Display::Tags);
|
||||
Tags->SetItemDisplayerUserData(TagTypes);
|
||||
Tags->SetGetStringFunction(TagToString);
|
||||
Tags->SetGetStringFunctionUserData(TagTypes);
|
||||
|
||||
w = LeftColumn;
|
||||
}
|
||||
@@ -734,6 +739,16 @@ void TagEditor::GetSelectedSongs(MPD::SongList &v)
|
||||
}
|
||||
}
|
||||
|
||||
void TagEditor::ApplyFilter(const std::string &s)
|
||||
{
|
||||
if (w == Dirs)
|
||||
Dirs->ApplyFilter(s, 1);
|
||||
else if (w == Albums)
|
||||
Albums->ApplyFilter(s);
|
||||
else if (w == Tags)
|
||||
Tags->ApplyFilter(s);
|
||||
}
|
||||
|
||||
List *TagEditor::GetList()
|
||||
{
|
||||
if (w == LeftColumn)
|
||||
@@ -961,6 +976,53 @@ void TagEditor::LowerAllLetters(Song &s)
|
||||
s.SetComment(conv);
|
||||
}
|
||||
|
||||
std::string TagEditor::TagToString(const MPD::Song &s, void *data)
|
||||
{
|
||||
std::string result;
|
||||
switch (static_cast<Menu<string> *>(data)->Choice())
|
||||
{
|
||||
case 0:
|
||||
result = s.GetTitle();
|
||||
break;
|
||||
case 1:
|
||||
result = s.GetArtist();
|
||||
break;
|
||||
case 2:
|
||||
result = s.GetAlbum();
|
||||
break;
|
||||
case 3:
|
||||
result = s.GetYear();
|
||||
break;
|
||||
case 4:
|
||||
result = s.GetTrack();
|
||||
break;
|
||||
case 5:
|
||||
result = s.GetGenre();
|
||||
break;
|
||||
case 6:
|
||||
result = s.GetComposer();
|
||||
break;
|
||||
case 7:
|
||||
result = s.GetPerformer();
|
||||
break;
|
||||
case 8:
|
||||
result = s.GetDisc();
|
||||
break;
|
||||
case 9:
|
||||
result = s.GetComment();
|
||||
break;
|
||||
case 11:
|
||||
if (s.GetNewName().empty())
|
||||
result = s.GetName();
|
||||
else
|
||||
result = s.GetName() + " -> " + s.GetNewName();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result.empty() ? Config.empty_tag : result;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
const string patterns_list_file = config_dir + "patterns.list";
|
||||
|
||||
@@ -60,6 +60,8 @@ extern TinyTagEditor *myTinyTagEditor;
|
||||
class TagEditor : public Screen<Window>
|
||||
{
|
||||
public:
|
||||
TagEditor() : itsBrowsedDir("/") { }
|
||||
|
||||
virtual void Init();
|
||||
virtual void Resize();
|
||||
virtual void SwitchTo();
|
||||
@@ -78,6 +80,8 @@ class TagEditor : public Screen<Window>
|
||||
virtual void ReverseSelection() { Tags->ReverseSelection(); }
|
||||
virtual void GetSelectedSongs(MPD::SongList &);
|
||||
|
||||
virtual void ApplyFilter(const std::string &);
|
||||
|
||||
virtual List *GetList();
|
||||
|
||||
void NextColumn();
|
||||
@@ -99,6 +103,8 @@ class TagEditor : public Screen<Window>
|
||||
static void CapitalizeFirstLetters(MPD::Song &);
|
||||
static void LowerAllLetters(MPD::Song &);
|
||||
|
||||
static std::string TagToString(const MPD::Song &, void *);
|
||||
|
||||
std::string itsBrowsedDir;
|
||||
std::string itsHighlightedDir;
|
||||
|
||||
|
||||
@@ -424,7 +424,7 @@ string Window::GetString(const string &base, size_t length, size_t width, bool e
|
||||
mvwhline(itsWindow, y, minx, '*', maxx-minx);
|
||||
|
||||
if (itsGetStringHelper)
|
||||
itsGetStringHelper();
|
||||
itsGetStringHelper(tmp);
|
||||
|
||||
wmove(itsWindow, y, x);
|
||||
input = wgetch(itsWindow);
|
||||
|
||||
@@ -52,7 +52,7 @@ enum Format { fmtNone = 100, fmtBold, fmtBoldEnd, fmtReverse, fmtReverseEnd, fmt
|
||||
enum Border { brNone, brBlack, brRed, brGreen, brYellow, brBlue, brMagenta, brCyan, brWhite };
|
||||
enum Where { wUp, wDown, wPageUp, wPageDown, wHome, wEnd };
|
||||
|
||||
typedef void (*GetStringHelper)();
|
||||
typedef void (*GetStringHelper)(const std::wstring &);
|
||||
|
||||
void InitScreen(bool);
|
||||
void DestroyScreen();
|
||||
|
||||
Reference in New Issue
Block a user