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:
Andrzej Rybczak
2009-02-17 17:57:55 +01:00
parent 2cb9b46135
commit 5d2daab416
27 changed files with 472 additions and 171 deletions

View File

@@ -82,6 +82,8 @@
#
#key_update_db = 'u'
#
#key_apply_filter = 6
#
#key_find_forward = '/'
#
#key_find_backward = '?'

View File

@@ -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 "";
}
}
}

View File

@@ -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;

View File

@@ -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";

View File

@@ -24,6 +24,7 @@
#include "mpdpp.h"
#include "ncmpcpp.h"
#include "settings.h"
#include "status.h"
bool ConnectToMPD();
void ParseArgv(int, char **);

View File

@@ -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())

View File

@@ -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;

View File

@@ -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 : "";
}

View File

@@ -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

View File

@@ -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),

View File

@@ -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 };

View File

@@ -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))
{

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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)

View File

@@ -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];

View File

@@ -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;

View File

@@ -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

View File

@@ -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";

View File

@@ -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;

View File

@@ -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);

View File

@@ -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();