From 56467eaac658b34a66aef583fc489331dfd5a7c8 Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Sat, 12 Nov 2011 19:47:47 +0100 Subject: [PATCH] new feature: support for merging screens together --- doc/config | 7 ++ doc/ncmpcpp.1 | 5 +- src/Makefile.am | 6 +- src/browser.cpp | 20 +++-- src/browser.h | 3 + src/clock.cpp | 62 ++++++++++----- src/clock.h | 5 ++ src/display.cpp | 13 ++-- src/display.h | 2 +- src/global.h | 2 + src/help.cpp | 13 +++- src/help.h | 3 + src/helpers.cpp | 25 ++++++ src/helpers.h | 3 + src/lastfm.cpp | 17 ++-- src/lastfm.h | 3 + src/lyrics.cpp | 22 +++++- src/lyrics.h | 3 + src/media_library.cpp | 45 +++++++---- src/media_library.h | 8 +- src/ncmpcpp.cpp | 168 +++++++++++++++++++++------------------- src/outputs.cpp | 13 +++- src/outputs.h | 3 + src/playlist.cpp | 22 ++++-- src/playlist.h | 3 + src/playlist_editor.cpp | 33 ++++++-- src/playlist_editor.h | 8 +- src/screen.cpp | 107 +++++++++++++++++++++++++ src/screen.h | 35 ++++++++- src/search_engine.cpp | 16 ++-- src/search_engine.h | 3 + src/sel_items_adder.h | 3 + src/server_info.h | 3 + src/settings.cpp | 9 +++ src/settings.h | 3 + src/song_info.cpp | 17 ++-- src/song_info.h | 3 + src/status.cpp | 28 +++++-- src/tag_editor.cpp | 50 ++++++++---- src/tag_editor.h | 10 ++- src/tiny_tag_editor.cpp | 19 +++-- src/tiny_tag_editor.h | 3 + src/visualizer.cpp | 22 ++++-- src/visualizer.h | 3 + 44 files changed, 633 insertions(+), 218 deletions(-) create mode 100644 src/screen.cpp diff --git a/doc/config b/doc/config index 58ac479f..e1af1baf 100644 --- a/doc/config +++ b/doc/config @@ -349,6 +349,13 @@ #screen_switcher_mode = "sequence: 2 -> 3" # ## +## Default width of locked screen (in %). +## Acceptable values are from 20 to 80. +## +# +#locked_screen_width_part = "50" +# +## ## Note: You can define startup screen for ncmpcpp ## by choosing screen number from the list above. ## diff --git a/doc/ncmpcpp.1 b/doc/ncmpcpp.1 index 850027d9..0e69d84c 100644 --- a/doc/ncmpcpp.1 +++ b/doc/ncmpcpp.1 @@ -249,9 +249,12 @@ If set to "playlist", Search engine will perform searching in current MPD playli .B display_screens_numbers_on_start = yes/no If enabled, screens' names and their keybindings will be shown in header window until key is pressed, otherwise they won't be displayed at all. .TP -.B screen_switcher_previous = SWITCHER_MODE +.B screen_switcher_mode = SWITCHER_MODE If set to "previous", key_screen_switcher will switch between current and last used screen. If set to "sequence: user_defined_sequence", it will switch between given sequence of screens. Syntax clarification can be found in example config file. .TP +.B locked_screen_width_part = 20-80 +If you want to lock a screen, ncmpcpp asks for % of locked screen's width to be reserved before that and provides a default value, which is the one you can set here. +.TP .B startup_screen = SCREEN_NUMBER Screen that has to be displayed at start (playlist by default). .TP diff --git a/src/Makefile.am b/src/Makefile.am index 8c86a601..b2ca35ab 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,9 +2,9 @@ bin_PROGRAMS = ncmpcpp ncmpcpp_SOURCES = browser.cpp charset.cpp clock.cpp conv.cpp curl_handle.cpp \ display.cpp error.cpp help.cpp helpers.cpp lastfm.cpp lastfm_service.cpp lyrics.cpp \ lyrics_fetcher.cpp media_library.cpp menu.cpp mpdpp.cpp ncmpcpp.cpp outputs.cpp \ - playlist.cpp playlist_editor.cpp scrollpad.cpp search_engine.cpp sel_items_adder.cpp \ - server_info.cpp settings.cpp song.cpp song_info.cpp status.cpp tag_editor.cpp \ - tiny_tag_editor.cpp tolower.cpp visualizer.cpp window.cpp + playlist.cpp playlist_editor.cpp screen.cpp scrollpad.cpp search_engine.cpp \ + sel_items_adder.cpp server_info.cpp settings.cpp song.cpp song_info.cpp status.cpp \ + tag_editor.cpp tiny_tag_editor.cpp tolower.cpp visualizer.cpp window.cpp # set the include path found by configure INCLUDES= $(all_includes) diff --git a/src/browser.cpp b/src/browser.cpp index 29e06d00..c588f65a 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -59,7 +59,7 @@ void Browser::Init() { static Display::ScreenFormat sf = { this, &Config.song_list_format }; - w = new Menu(0, MainStartY, COLS, MainHeight, Config.columns_in_browser && Config.titles_visibility ? Display::Columns() : "", Config.main_color, brNone); + w = new Menu(0, MainStartY, COLS, MainHeight, Config.columns_in_browser && Config.titles_visibility ? Display::Columns(COLS) : "", Config.main_color, brNone); w->HighlightColor(Config.main_highlight_color); w->CyclicScrolling(Config.use_cyclic_scrolling); w->CenteredCursor(Config.centered_cursor); @@ -73,14 +73,19 @@ void Browser::Init() void Browser::Resize() { - w->Resize(COLS, MainHeight); - w->MoveTo(0, MainStartY); - w->SetTitle(Config.columns_in_browser && Config.titles_visibility ? Display::Columns() : ""); + size_t x_offset, width; + GetWindowResizeParams(x_offset, width); + w->Resize(width, MainHeight); + w->MoveTo(x_offset, MainStartY); + w->SetTitle(Config.columns_in_browser && Config.titles_visibility ? Display::Columns(w->GetWidth()) : ""); hasToBeResized = 0; } void Browser::SwitchTo() { + using Global::myLockedScreen; + using Global::myInactiveScreen; + if (myScreen == this) { # ifndef WIN32 @@ -91,7 +96,10 @@ void Browser::SwitchTo() if (!isInitialized) Init(); - if (hasToBeResized) + if (myLockedScreen) + UpdateInactiveScreen(this); + + if (hasToBeResized || myLockedScreen) Resize(); if (isLocal()) // local browser doesn't support sorting by mtime @@ -108,7 +116,7 @@ void Browser::SwitchTo() std::basic_string Browser::Title() { std::basic_string result = U("Browse: "); - result += Scroller(TO_WSTRING(itsBrowsedDir), itsScrollBeginning, w->GetWidth()-result.length()-(Config.new_design ? 2 : Global::VolumeState.length())); + result += Scroller(TO_WSTRING(itsBrowsedDir), itsScrollBeginning, COLS-result.length()-(Config.new_design ? 2 : Global::VolumeState.length())); return result; } diff --git a/src/browser.h b/src/browser.h index 343c611f..8e135536 100644 --- a/src/browser.h +++ b/src/browser.h @@ -50,6 +50,8 @@ class Browser : public Screen< Menu > virtual List *GetList() { return w; } + virtual bool isMergable() { return true; } + const std::string &CurrentDir() { return itsBrowsedDir; } bool isLocal() { return itsBrowseLocally; } @@ -65,6 +67,7 @@ class Browser : public Screen< Menu > protected: virtual void Init(); + virtual bool isLockable() { return true; } private: static bool hasSupportedExtension(const std::string &); diff --git a/src/clock.cpp b/src/clock.cpp index 720d4552..7e162818 100644 --- a/src/clock.cpp +++ b/src/clock.cpp @@ -54,47 +54,57 @@ void Clock::Init() { Width = Config.clock_display_seconds ? 60 : 40; + itsPane = new Window(0, MainStartY, COLS, MainHeight, "", Config.main_color, brNone); w = new Window((COLS-Width)/2, (MainHeight-Height)/2+MainStartY, Width, Height-1, "", Config.main_color, Border(Config.main_color)); isInitialized = 1; } void Clock::Resize() { - if (Width <= size_t(COLS) && Height <= MainHeight) - { - w->MoveTo((COLS-Width)/2, (MainHeight-Height)/2+MainStartY); - if (myScreen == this) - { - if (myPlaylist->hasToBeResized) - myPlaylist->Resize(); - myPlaylist->Items->Hide(); - w->Display(); - } - } + size_t x_offset, width; + GetWindowResizeParams(x_offset, width); + + // used for clearing area out of clock window while resizing terminal + itsPane->Resize(width, MainHeight); + itsPane->MoveTo(x_offset, MainStartY); + itsPane->Refresh(); + + if (Width <= width && Height <= MainHeight) + w->MoveTo(x_offset+(width-Width)/2, MainStartY+(MainHeight-Height)/2); } void Clock::SwitchTo() { - if (Width > size_t(COLS) || Height > MainHeight) - { - ShowMessage("Screen is too small to display clock!"); - return; - } + using Global::myLockedScreen; + if (myScreen == this) return; if (!isInitialized) Init(); - if (hasToBeResized) + if (myLockedScreen) + UpdateInactiveScreen(this); + + size_t x_offset, width; + GetWindowResizeParams(x_offset, width, false); + if (Width > width || Height > MainHeight) + { + ShowMessage("Screen is too small to display clock!"); + if (myLockedScreen) + UpdateInactiveScreen(myLockedScreen); + return; + } + + if (hasToBeResized || myLockedScreen) Resize(); if (myScreen != this && myScreen->isTabbable()) Global::myPrevScreen = myScreen; myScreen = this; - myPlaylist->Items->Hide(); Global::RedrawHeader = 1; Prepare(); + itsPane->Refresh(); // clearing screen apparently fixes the problem with last digits being misrendered w->Clear(); w->Display(); @@ -107,8 +117,20 @@ std::basic_string Clock::Title() void Clock::Update() { - if (Width > size_t(COLS) || Height > MainHeight) - myPlaylist->SwitchTo(); + if (Width > itsPane->GetWidth() || Height > MainHeight) + { + using Global::myLockedScreen; + using Global::myInactiveScreen; + + if (myLockedScreen) + { + if (myInactiveScreen != myLockedScreen) + myScreen = myInactiveScreen; + myLockedScreen->SwitchTo(); + } + else + myPlaylist->SwitchTo(); + } static timeval past = { 0, 0 }; gettimeofday(&past, 0); diff --git a/src/clock.h b/src/clock.h index 17c0bffa..4c7d1f85 100644 --- a/src/clock.h +++ b/src/clock.h @@ -50,10 +50,15 @@ class Clock : public Screen virtual List *GetList() { return 0; } + virtual bool isMergable() { return true; } + protected: virtual void Init(); + virtual bool isLockable() { return false; } private: + Window *itsPane; + static void Prepare(); static void Set(int, int); diff --git a/src/display.cpp b/src/display.cpp index d62162a5..ac6b4bec 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -69,7 +69,7 @@ namespace } } -std::string Display::Columns() +std::string Display::Columns(size_t list_width) { if (Config.columns.empty()) return ""; @@ -86,11 +86,11 @@ std::string Display::Columns() for (std::vector::const_iterator it = Config.columns.begin(); it != Config.columns.end(); ++it) { if (it == Config.columns.end()-1) - width = COLS-where; + width = list_width-where; else if (last_fixed && it == next2last) - width = COLS-where-1-(++next2last)->width; + width = list_width-where-1-(++next2last)->width; else - width = it->width*(it->fixed ? 1 : COLS/100.0); + width = it->width*(it->fixed ? 1 : list_width/100.0); std::basic_string tag; if (it->type.length() >= 1 && it->name.empty()) @@ -122,6 +122,7 @@ std::string Display::Columns() else result.resize(std::min(where+1, size_t(COLS)), ' '); } + result.resize(list_width); return TO_STRING(result); } @@ -175,9 +176,9 @@ void Display::SongsInColumns(const MPD::Song &s, void *data, Menu *me if (it == Config.columns.end()-1) width = menu->GetWidth()-where; else if (last_fixed && it == next2last) - width = COLS-where-1-(++next2last)->width; + width = menu->GetWidth()-where-1-(++next2last)->width; else - width = it->width*(it->fixed ? 1 : COLS/100.0); + width = it->width*(it->fixed ? 1 : menu->GetWidth()/100.0); MPD::Song::GetFunction get = 0; diff --git a/src/display.h b/src/display.h index 8e132b77..f1fdbdc5 100644 --- a/src/display.h +++ b/src/display.h @@ -34,7 +34,7 @@ namespace Display std::string *format; }; - std::string Columns(); + std::string Columns(size_t); template void Generic(const T &t, void *, Menu *menu) { diff --git a/src/global.h b/src/global.h index 266de750..59bf630f 100644 --- a/src/global.h +++ b/src/global.h @@ -30,6 +30,8 @@ namespace Global extern BasicScreen *myScreen; extern BasicScreen *myOldScreen; // for info, lyrics, popups extern BasicScreen *myPrevScreen; // "real" screen switching (browser, search, etc.) + extern BasicScreen *myLockedScreen; // points at the screen that was locked (or is null if no screen is locked) + extern BasicScreen *myInactiveScreen; // points at inactive screen, if locking was enabled and two screens are displayed extern Window *wHeader; extern Window *wFooter; diff --git a/src/help.cpp b/src/help.cpp index 1becdc1f..70d907f1 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -40,14 +40,17 @@ void Help::Init() void Help::Resize() { - w->Resize(COLS, MainHeight); - w->MoveTo(0, MainStartY); + size_t x_offset, width; + GetWindowResizeParams(x_offset, width); + w->Resize(width, MainHeight); + w->MoveTo(x_offset, MainStartY); hasToBeResized = 0; } void Help::SwitchTo() { using Global::myScreen; + using Global::myLockedScreen; if (myScreen == this) return; @@ -55,7 +58,10 @@ void Help::SwitchTo() if (!isInitialized) Init(); - if (hasToBeResized) + if (myLockedScreen) + UpdateInactiveScreen(this); + + if (hasToBeResized || myLockedScreen) Resize(); if (myScreen != this && myScreen->isTabbable()) @@ -211,6 +217,7 @@ void Help::GetKeybindings() *w << DisplayKeys(Key.ToggleFindMode) << "Toggle find mode (normal/wrapped)\n"; *w << DisplayKeys(Key.GoToContainingDir) << "Locate song in browser\n"; *w << DisplayKeys(Key.GoToMediaLibrary) << "Locate current song in media library\n"; + *w << DisplayKeys(Key.ToggleScreenLock) << "Lock/unlock current screen\n"; # ifdef HAVE_TAGLIB_H *w << DisplayKeys(Key.GoToTagEditor) << "Locate current song in tag editor\n"; # endif // HAVE_TAGLIB_H diff --git a/src/help.h b/src/help.h index 0a571c83..2168c558 100644 --- a/src/help.h +++ b/src/help.h @@ -40,8 +40,11 @@ class Help : public Screen virtual List *GetList() { return 0; } + virtual bool isMergable() { return true; } + protected: virtual void Init(); + virtual bool isLockable() { return true; } private: std::string DisplayKeys(int *, int = 2); diff --git a/src/helpers.cpp b/src/helpers.cpp index e4a0d371..5fcd140e 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -461,3 +461,28 @@ std::basic_string Scroller(const std::basic_string &str, s return result; } +bool SwitchToNextColumn(BasicScreen *screen) +{ + if (screen == myLibrary) + return myLibrary->NextColumn(); + else if (screen == myPlaylistEditor) + return myPlaylistEditor->NextColumn(); +# ifdef HAVE_TAGLIB_H + else if (screen == myTagEditor) + return myTagEditor->NextColumn(); +# endif // HAVE_TAGLIB_H + return false; +} + +bool SwitchToPrevColumn(BasicScreen *screen) +{ + if (screen == myLibrary) + return myLibrary->PrevColumn(); + else if (screen == myPlaylistEditor) + return myPlaylistEditor->PrevColumn(); +# ifdef HAVE_TAGLIB_H + else if (screen == myTagEditor) + return myTagEditor->PrevColumn(); +# endif // HAVE_TAGLIB_H + return false; +} diff --git a/src/helpers.h b/src/helpers.h index f6a0f80d..82ff3457 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -219,5 +219,8 @@ std::string GetLineValue(std::string &, char = '"', char = '"', bool = 0); std::basic_string Scroller(const std::basic_string &str, size_t &pos, size_t width); +bool SwitchToNextColumn(BasicScreen *); +bool SwitchToPrevColumn(BasicScreen *); + #endif diff --git a/src/lastfm.cpp b/src/lastfm.cpp index d6c662e8..fda59e72 100644 --- a/src/lastfm.cpp +++ b/src/lastfm.cpp @@ -38,8 +38,6 @@ using Global::MainHeight; using Global::MainStartY; -using Global::myScreen; -using Global::myOldScreen; Lastfm *myLastfm = new Lastfm; @@ -51,8 +49,10 @@ void Lastfm::Init() void Lastfm::Resize() { - w->Resize(COLS, MainHeight); - w->MoveTo(0, MainStartY); + size_t x_offset, width; + GetWindowResizeParams(x_offset, width); + w->Resize(width, MainHeight); + w->MoveTo(x_offset, MainStartY); hasToBeResized = 0; } @@ -79,13 +79,20 @@ void Lastfm::Take() void Lastfm::SwitchTo() { + using Global::myScreen; + using Global::myOldScreen; + using Global::myLockedScreen; + if (myScreen == this) return myOldScreen->SwitchTo(); if (!isInitialized) Init(); - if (hasToBeResized) + if (myLockedScreen) + UpdateInactiveScreen(this); + + if (hasToBeResized || myLockedScreen) Resize(); // get an old info if it waits diff --git a/src/lastfm.h b/src/lastfm.h index 6bbdc601..82d8060a 100644 --- a/src/lastfm.h +++ b/src/lastfm.h @@ -52,6 +52,8 @@ class Lastfm : public Screen virtual List *GetList() { return 0; } + virtual bool isMergable() { return true; } + void Refetch(); bool isDownloading() { return isDownloadInProgress && !isReadyToTake; } @@ -59,6 +61,7 @@ class Lastfm : public Screen protected: virtual void Init(); + virtual bool isLockable() { return false; } private: std::basic_string itsTitle; diff --git a/src/lyrics.cpp b/src/lyrics.cpp index cb51202f..75b494d7 100644 --- a/src/lyrics.cpp +++ b/src/lyrics.cpp @@ -65,8 +65,10 @@ void Lyrics::Init() void Lyrics::Resize() { - w->Resize(COLS, MainHeight); - w->MoveTo(0, MainStartY); + size_t x_offset, width; + GetWindowResizeParams(x_offset, width); + w->Resize(width, MainHeight); + w->MoveTo(x_offset, MainStartY); hasToBeResized = 0; } @@ -98,6 +100,9 @@ void Lyrics::Update() void Lyrics::SwitchTo() { + using Global::myLockedScreen; + using Global::myInactiveScreen; + if (myScreen == this) return myOldScreen->SwitchTo(); @@ -134,14 +139,25 @@ void Lyrics::SwitchTo() Global::RedrawHeader = 1; } else + { ShowMessage("Song must have both artist and title tag set!"); + return; + } + } + // if we resize for locked screen, we have to do that in the end since + // fetching lyrics may fail (eg. if tags are missing) and we don't want + // to adjust screen size then. + if (myLockedScreen) + { + UpdateInactiveScreen(this); + Resize(); } } std::basic_string Lyrics::Title() { std::basic_string result = U("Lyrics: "); - result += Scroller(TO_WSTRING(itsSong.toString("{%a - %t}")), itsScrollBegin, w->GetWidth()-result.length()-(Config.new_design ? 2 : Global::VolumeState.length())); + result += Scroller(TO_WSTRING(itsSong.toString("{%a - %t}")), itsScrollBegin, COLS-result.length()-(Config.new_design ? 2 : Global::VolumeState.length())); return result; } diff --git a/src/lyrics.h b/src/lyrics.h index 965db7bb..d82c38cd 100644 --- a/src/lyrics.h +++ b/src/lyrics.h @@ -51,6 +51,8 @@ class Lyrics : public Screen virtual List *GetList() { return 0; } + virtual bool isMergable() { return true; } + void Edit(); void Refetch(); @@ -63,6 +65,7 @@ class Lyrics : public Screen protected: virtual void Init(); + virtual bool isLockable() { return false; } private: void Load(); diff --git a/src/media_library.cpp b/src/media_library.cpp index aa6fc394..7095a902 100644 --- a/src/media_library.cpp +++ b/src/media_library.cpp @@ -36,6 +36,7 @@ using Global::myScreen; MediaLibrary *myLibrary = new MediaLibrary; bool MediaLibrary::hasTwoColumns; +size_t MediaLibrary::itsLeftColStartX; size_t MediaLibrary::itsLeftColWidth; size_t MediaLibrary::itsMiddleColWidth; size_t MediaLibrary::itsMiddleColStartX; @@ -93,27 +94,30 @@ void MediaLibrary::Init() void MediaLibrary::Resize() { + size_t x_offset, width; + GetWindowResizeParams(x_offset, width); if (!hasTwoColumns) { - itsLeftColWidth = COLS/3-1; - itsMiddleColStartX = itsLeftColWidth+1; - itsMiddleColWidth = COLS/3; - itsRightColStartX = itsLeftColWidth+itsMiddleColWidth+2; - itsRightColWidth = COLS-COLS/3*2-1; + itsLeftColStartX = x_offset; + itsLeftColWidth = width/3-1; + itsMiddleColStartX = itsLeftColStartX+itsLeftColWidth+1; + itsMiddleColWidth = width/3; + itsRightColStartX = itsMiddleColStartX+itsMiddleColWidth+1; + itsRightColWidth = width-width/3*2-1; } else { - itsMiddleColStartX = 0; - itsMiddleColWidth = COLS/2; - itsRightColStartX = itsMiddleColWidth+1; - itsRightColWidth = COLS-itsMiddleColWidth-1; + itsMiddleColStartX = x_offset; + itsMiddleColWidth = width/2; + itsRightColStartX = x_offset+itsMiddleColWidth+1; + itsRightColWidth = width-itsMiddleColWidth-1; } Artists->Resize(itsLeftColWidth, MainHeight); Albums->Resize(itsMiddleColWidth, MainHeight); Songs->Resize(itsRightColWidth, MainHeight); - Artists->MoveTo(0, MainStartY); + Artists->MoveTo(itsLeftColStartX, MainStartY); Albums->MoveTo(itsMiddleColStartX, MainStartY); Songs->MoveTo(itsRightColStartX, MainStartY); @@ -136,6 +140,8 @@ void MediaLibrary::Refresh() void MediaLibrary::SwitchTo() { + using Global::myLockedScreen; + if (myScreen == this) { if (Config.media_library_disable_two_column_mode) @@ -169,7 +175,10 @@ void MediaLibrary::SwitchTo() if (!isInitialized) Init(); - if (hasToBeResized) + if (myLockedScreen) + UpdateInactiveScreen(this); + + if (hasToBeResized || myLockedScreen) Resize(); if (myScreen != this && myScreen->isTabbable()) @@ -544,18 +553,18 @@ void MediaLibrary::ApplyFilter(const std::string &s) GetList()->ApplyFilter(s, 0, REG_ICASE | Config.regex_type); } -void MediaLibrary::NextColumn() +bool MediaLibrary::NextColumn() { if (w == Artists) { if (!hasTwoColumns && Songs->ReallyEmpty()) - return; + return false; Artists->HighlightColor(Config.main_highlight_color); w->Refresh(); w = Albums; Albums->HighlightColor(Config.active_column_color); if (!Albums->ReallyEmpty()) - return; + return true; } if (w == Albums && !Songs->ReallyEmpty()) { @@ -563,10 +572,12 @@ void MediaLibrary::NextColumn() w->Refresh(); w = Songs; Songs->HighlightColor(Config.active_column_color); + return true; } + return false; } -void MediaLibrary::PrevColumn() +bool MediaLibrary::PrevColumn() { if (w == Songs) { @@ -575,7 +586,7 @@ void MediaLibrary::PrevColumn() w = Albums; Albums->HighlightColor(Config.active_column_color); if (!Albums->ReallyEmpty()) - return; + return true; } if (w == Albums && !hasTwoColumns) { @@ -583,7 +594,9 @@ void MediaLibrary::PrevColumn() w->Refresh(); w = Artists; Artists->HighlightColor(Config.active_column_color); + return true; } + return false; } void MediaLibrary::LocateSong(const MPD::Song &s) diff --git a/src/media_library.h b/src/media_library.h index 446861f4..b3788e6d 100644 --- a/src/media_library.h +++ b/src/media_library.h @@ -66,9 +66,11 @@ class MediaLibrary : public Screen virtual List *GetList(); + virtual bool isMergable() { return true; } + int Columns() { return hasTwoColumns ? 2 : 3; } - void NextColumn(); - void PrevColumn(); + bool NextColumn(); + bool PrevColumn(); void LocateSong(const MPD::Song &); @@ -78,6 +80,7 @@ class MediaLibrary : public Screen protected: virtual void Init(); + virtual bool isLockable() { return true; } private: void AddToPlaylist(bool); @@ -92,6 +95,7 @@ class MediaLibrary : public Screen static bool SortAllTracks(MPD::Song *, MPD::Song *); static bool hasTwoColumns; + static size_t itsLeftColStartX; static size_t itsLeftColWidth; static size_t itsMiddleColWidth; static size_t itsMiddleColStartX; diff --git a/src/ncmpcpp.cpp b/src/ncmpcpp.cpp index 9500d95a..2d9733be 100644 --- a/src/ncmpcpp.cpp +++ b/src/ncmpcpp.cpp @@ -75,6 +75,8 @@ using namespace MPD; BasicScreen *Global::myScreen; BasicScreen *Global::myOldScreen; BasicScreen *Global::myPrevScreen; +BasicScreen *Global::myLockedScreen; +BasicScreen *Global::myInactiveScreen; Window *Global::wHeader; Window *Global::wFooter; @@ -107,6 +109,40 @@ namespace } } + void set_resize_flags() + { + myHelp->hasToBeResized = 1; + myPlaylist->hasToBeResized = 1; + myBrowser->hasToBeResized = 1; + mySearcher->hasToBeResized = 1; + myLibrary->hasToBeResized = 1; + myPlaylistEditor->hasToBeResized = 1; + myLyrics->hasToBeResized = 1; + mySelectedItemsAdder->hasToBeResized = 1; + mySongInfo->hasToBeResized = 1; + +# ifdef HAVE_CURL_CURL_H + myLastfm->hasToBeResized = 1; +# endif // HAVE_CURL_CURL_H + +# ifdef HAVE_TAGLIB_H + myTinyTagEditor->hasToBeResized = 1; + myTagEditor->hasToBeResized = 1; +# endif // HAVE_TAGLIB_H + +# ifdef ENABLE_VISUALIZER + myVisualizer->hasToBeResized = 1; +# endif // ENABLE_VISUALIZER + +# ifdef ENABLE_OUTPUTS + myOutputs->hasToBeResized = 1; +# endif // ENABLE_OUTPUTS + +# ifdef ENABLE_CLOCK + myClock->hasToBeResized = 1; +# endif // ENABLE_CLOCK + } + void resize_screen() { order_resize = 0; @@ -139,38 +175,9 @@ namespace if (!Config.statusbar_visibility) MainHeight++; - myHelp->hasToBeResized = 1; - myPlaylist->hasToBeResized = 1; - myBrowser->hasToBeResized = 1; - mySearcher->hasToBeResized = 1; - myLibrary->hasToBeResized = 1; - myPlaylistEditor->hasToBeResized = 1; - myLyrics->hasToBeResized = 1; - mySelectedItemsAdder->hasToBeResized = 1; - mySongInfo->hasToBeResized = 1; + set_resize_flags(); -# ifdef HAVE_CURL_CURL_H - myLastfm->hasToBeResized = 1; -# endif // HAVE_CURL_CURL_H - -# ifdef HAVE_TAGLIB_H - myTinyTagEditor->hasToBeResized = 1; - myTagEditor->hasToBeResized = 1; -# endif // HAVE_TAGLIB_H - -# ifdef ENABLE_VISUALIZER - myVisualizer->hasToBeResized = 1; -# endif // ENABLE_VISUALIZER - -# ifdef ENABLE_OUTPUTS - myOutputs->hasToBeResized = 1; -# endif // ENABLE_OUTPUTS - -# ifdef ENABLE_CLOCK - myClock->hasToBeResized = 1; -# endif // ENABLE_CLOCK - - myScreen->Resize(); + ApplyToVisibleWindows(&BasicScreen::Resize); if (Config.header_visibility || Config.new_design) wHeader->Resize(COLS, header_height); @@ -179,7 +186,7 @@ namespace wFooter->MoveTo(0, footer_start_y); wFooter->Resize(COLS, Config.statusbar_visibility ? 2 : 1); - myScreen->Refresh(); + ApplyToVisibleWindows(&BasicScreen::Refresh); RedrawStatusbar = 1; StatusChanges changes; if (!Mpd.isPlaying() || design_changed) @@ -201,6 +208,7 @@ namespace ShowMessage("User interface: %s", Config.new_design ? "Alternative" : "Classic"); } wFooter->Refresh(); + refresh(); } # if !defined(WIN32) @@ -440,6 +448,7 @@ int main(int argc, char *argv[]) // header stuff end if (input != ERR) + //ApplyToVisibleWindows(&BasicScreen::RefreshWindow); myScreen->RefreshWindow(); wFooter->ReadKey(input); @@ -450,9 +459,6 @@ int main(int argc, char *argv[]) RedrawHeader = 1; title_allowed = 1; - if (myScreen == myPlaylist) - myPlaylist->EnableHighlighting(); - // key mapping beginning if (Keypressed(input, Key.Up)) @@ -607,53 +613,25 @@ int main(int argc, char *argv[]) { myScreen->SpacePressed(); } - else if (Keypressed(input, Key.PrevColumn) - && (myScreen == myLibrary - || myScreen == myPlaylistEditor -# ifdef HAVE_TAGLIB_H - || myScreen == myTagEditor -# endif // HAVE_TAGLIB_H) - ) - ) + else if (Keypressed(input, Key.PrevColumn) && SwitchToPrevColumn(myScreen)) { } + else if (Keypressed(input, Key.NextColumn) && SwitchToNextColumn(myScreen)) { } + else if (Keypressed(input, Key.PrevColumn) && myLockedScreen && myInactiveScreen && myScreen->isMergable()) { - if (myScreen == myLibrary) + if (myScreen != myLockedScreen) { - myLibrary->PrevColumn(); + myInactiveScreen = myScreen; + myScreen = myLockedScreen; + RedrawHeader = 1; } - else if (myScreen == myPlaylistEditor) - { - myPlaylistEditor->PrevColumn(); - } -# ifdef HAVE_TAGLIB_H - else if (myScreen == myTagEditor) - { - myTagEditor->PrevColumn(); - } -# endif // HAVE_TAGLIB_H } - else if (Keypressed(input, Key.NextColumn) - && (myScreen == myLibrary - || myScreen == myPlaylistEditor -# ifdef HAVE_TAGLIB_H - || myScreen == myTagEditor -# endif // HAVE_TAGLIB_H) - ) - ) + else if (Keypressed(input, Key.NextColumn) && myLockedScreen && myInactiveScreen && myScreen->isMergable()) { - if (myScreen == myLibrary) + if (myScreen == myLockedScreen) { - myLibrary->NextColumn(); + myScreen = myInactiveScreen; + myInactiveScreen = myLockedScreen; + RedrawHeader = 1; } - else if (myScreen == myPlaylistEditor) - { - myPlaylistEditor->NextColumn(); - } -# ifdef HAVE_TAGLIB_H - else if (myScreen == myTagEditor) - { - myTagEditor->NextColumn(); - } -# endif // HAVE_TAGLIB_H } else if (Keypressed(input, Key.VolumeUp)) { @@ -1397,7 +1375,7 @@ int main(int argc, char *argv[]) if (Config.columns_in_playlist) { myPlaylist->Items->SetItemDisplayer(Display::SongsInColumns); - myPlaylist->Items->SetTitle(Config.titles_visibility ? Display::Columns() : ""); + myPlaylist->Items->SetTitle(Config.titles_visibility ? Display::Columns(myPlaylist->Items->GetWidth()) : ""); myPlaylist->Items->SetGetStringFunction(Playlist::SongInColumnsToString); } else @@ -1411,7 +1389,7 @@ int main(int argc, char *argv[]) { 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 && Config.titles_visibility ? Display::Columns() : ""); + myBrowser->Main()->SetTitle(Config.columns_in_browser && Config.titles_visibility ? Display::Columns(myBrowser->Main()->GetWidth()) : ""); } else if (myScreen == mySearcher) @@ -1419,7 +1397,7 @@ int main(int argc, char *argv[]) Config.columns_in_search_engine = !Config.columns_in_search_engine; ShowMessage("Search engine display mode: %s", Config.columns_in_search_engine ? "Columns" : "Classic"); if (mySearcher->Main()->Size() > SearchEngine::StaticOptions) - mySearcher->Main()->SetTitle(Config.columns_in_search_engine && Config.titles_visibility ? Display::Columns() : ""); + mySearcher->Main()->SetTitle(Config.columns_in_search_engine && Config.titles_visibility ? Display::Columns(mySearcher->Main()->GetWidth()) : ""); } } else if (Keypressed(input, Key.ToggleSeparatorsInPlaylist)) @@ -1736,6 +1714,35 @@ int main(int argc, char *argv[]) if (s) myLibrary->LocateSong(*s); } + else if (Keypressed(input, Key.ToggleScreenLock)) + { + if (myLockedScreen != 0) + { + BasicScreen::Unlock(); + set_resize_flags(); + ShowMessage("Screen unlocked"); + } + else + { + LockStatusbar(); + Statusbar() << "% of the locked screen's width to be reserved (20-80): "; + std::string str_part = wFooter->GetString(IntoStr(Config.locked_screen_width_part*100)); + UnlockStatusbar(); + if (str_part.empty()) + continue; + unsigned part = StrToInt(str_part); + if (part < 20 || part > 80) + { + ShowMessage("Invalid number!"); + continue; + } + Config.locked_screen_width_part = part/100.0; + if (myScreen->Lock()) + ShowMessage("Screen locked (with %d%% width)", part); + else + ShowMessage("Screen cannot be locked"); + } + } # ifdef HAVE_TAGLIB_H else if (Keypressed(input, Key.GoToTagEditor)) { @@ -2353,10 +2360,13 @@ int main(int argc, char *argv[]) } } + if (myScreen == myPlaylist) + myPlaylist->EnableHighlighting(); + # ifdef ENABLE_VISUALIZER // visualizer sets timmeout to 40ms, but since only it needs such small // value, we should restore defalt one after switching to another screen. - if (wFooter->GetTimeout() < ncmpcpp_window_timeout && myScreen != myVisualizer) + if (wFooter->GetTimeout() < ncmpcpp_window_timeout && !isVisible(myVisualizer)) wFooter->SetTimeout(ncmpcpp_window_timeout); # endif // ENABLE_VISUALIZER } diff --git a/src/outputs.cpp b/src/outputs.cpp index eade3b68..afb1b29e 100644 --- a/src/outputs.cpp +++ b/src/outputs.cpp @@ -45,13 +45,18 @@ void Outputs::Init() void Outputs::SwitchTo() { + using Global::myLockedScreen; + if (myScreen == this) return; if (!isInitialized) Init(); - if (hasToBeResized) + if (myLockedScreen) + UpdateInactiveScreen(this); + + if (hasToBeResized || myLockedScreen) Resize(); if (myScreen != this && myScreen->isTabbable()) @@ -64,8 +69,10 @@ void Outputs::SwitchTo() void Outputs::Resize() { - w->Resize(COLS, MainHeight); - w->MoveTo(0, MainStartY); + size_t x_offset, width; + GetWindowResizeParams(x_offset, width); + w->Resize(width, MainHeight); + w->MoveTo(x_offset, MainStartY); hasToBeResized = 0; } diff --git a/src/outputs.h b/src/outputs.h index 64b5fd7e..f59eef1d 100644 --- a/src/outputs.h +++ b/src/outputs.h @@ -48,10 +48,13 @@ class Outputs : public Screen< Menu > virtual List *GetList() { return w; } + virtual bool isMergable() { return true; } + void FetchList(); protected: virtual void Init(); + virtual bool isLockable() { return true; } }; extern Outputs *myOutputs; diff --git a/src/playlist.cpp b/src/playlist.cpp index 17c6c459..f9b45ee0 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -49,7 +49,7 @@ void Playlist::Init() { static Display::ScreenFormat sf = { this, &Config.song_list_format }; - Items = new Menu(0, MainStartY, COLS, MainHeight, Config.columns_in_playlist && Config.titles_visibility ? Display::Columns() : "", Config.main_color, brNone); + Items = new Menu(0, MainStartY, COLS, MainHeight, Config.columns_in_playlist && Config.titles_visibility ? Display::Columns(COLS) : "", Config.main_color, brNone); Items->CyclicScrolling(Config.use_cyclic_scrolling); Items->CenteredCursor(Config.centered_cursor); Items->HighlightColor(Config.main_highlight_color); @@ -92,6 +92,8 @@ void Playlist::Init() void Playlist::SwitchTo() { using Global::myScreen; + using Global::myLockedScreen; + using Global::myInactiveScreen; if (myScreen == this) return; @@ -101,7 +103,10 @@ void Playlist::SwitchTo() itsScrollBegin = 0; - if (hasToBeResized) + if (myLockedScreen) + UpdateInactiveScreen(this); + + if (hasToBeResized || myLockedScreen) Resize(); if (myScreen != this && myScreen->isTabbable()) @@ -116,9 +121,12 @@ void Playlist::SwitchTo() void Playlist::Resize() { - Items->Resize(COLS, MainHeight); - Items->MoveTo(0, MainStartY); - Items->SetTitle(Config.columns_in_playlist && Config.titles_visibility ? Display::Columns() : ""); + size_t x_offset, width; + GetWindowResizeParams(x_offset, width); + Items->Resize(width, MainHeight); + Items->MoveTo(x_offset, MainStartY); + + Items->SetTitle(Config.columns_in_playlist && Config.titles_visibility ? Display::Columns(Items->GetWidth()) : ""); if (w == SortDialog) // if sorting window is active, playlist needs refreshing Items->Display(); @@ -126,7 +134,7 @@ void Playlist::Resize() if (Items->GetWidth() >= SortDialogWidth && MainHeight >= 5) { SortDialog->Resize(SortDialogWidth, SortDialogHeight); - SortDialog->MoveTo((COLS-SortDialogWidth)/2, (MainHeight-SortDialogHeight)/2+MainStartY); + SortDialog->MoveTo(x_offset+(width-SortDialogWidth)/2, (MainHeight-SortDialogHeight)/2+MainStartY); } else // if screen is too low to display sorting window, fall back to items list w = Items; @@ -139,7 +147,7 @@ std::basic_string Playlist::Title() std::basic_string result = U("Playlist "); if (ReloadTotalLength || ReloadRemaining) itsBufferedStats = TotalLength(); - result += Scroller(TO_WSTRING(itsBufferedStats), itsScrollBegin, Items->GetWidth()-result.length()-(Config.new_design ? 2 : Global::VolumeState.length())); + result += Scroller(TO_WSTRING(itsBufferedStats), itsScrollBegin, COLS-result.length()-(Config.new_design ? 2 : Global::VolumeState.length())); return result; } diff --git a/src/playlist.h b/src/playlist.h index f454da7e..df275d0f 100644 --- a/src/playlist.h +++ b/src/playlist.h @@ -55,6 +55,8 @@ class Playlist : public Screen virtual List *GetList() { return w == Items ? Items : 0; } + virtual bool isMergable() { return true; } + bool isPlaying() { return NowPlaying >= 0 && !Items->Empty(); } const MPD::Song *NowPlayingSong(); @@ -85,6 +87,7 @@ class Playlist : public Screen protected: virtual void Init(); + virtual bool isLockable() { return true; } private: std::string TotalLength(); diff --git a/src/playlist_editor.cpp b/src/playlist_editor.cpp index cc81c891..7c588791 100644 --- a/src/playlist_editor.cpp +++ b/src/playlist_editor.cpp @@ -35,6 +35,7 @@ using Global::MainStartY; PlaylistEditor *myPlaylistEditor = new PlaylistEditor; +size_t PlaylistEditor::LeftColumnStartX; size_t PlaylistEditor::LeftColumnWidth; size_t PlaylistEditor::RightColumnStartX; size_t PlaylistEditor::RightColumnWidth; @@ -70,14 +71,18 @@ void PlaylistEditor::Init() void PlaylistEditor::Resize() { - LeftColumnWidth = COLS/3-1; - RightColumnStartX = LeftColumnWidth+1; - RightColumnWidth = COLS-LeftColumnWidth-1; + size_t x_offset, width; + GetWindowResizeParams(x_offset, width); + + LeftColumnStartX = x_offset; + LeftColumnWidth = width/3-1; + RightColumnStartX = LeftColumnStartX+LeftColumnWidth+1; + RightColumnWidth = width-LeftColumnWidth-1; Playlists->Resize(LeftColumnWidth, MainHeight); Content->Resize(RightColumnWidth, MainHeight); - Playlists->MoveTo(0, MainStartY); + Playlists->MoveTo(LeftColumnStartX, MainStartY); Content->MoveTo(RightColumnStartX, MainStartY); hasToBeResized = 0; @@ -98,6 +103,7 @@ void PlaylistEditor::Refresh() void PlaylistEditor::SwitchTo() { using Global::myScreen; + using Global::myLockedScreen; if (myScreen == this) return; @@ -105,7 +111,10 @@ void PlaylistEditor::SwitchTo() if (!isInitialized) Init(); - if (hasToBeResized) + if (myLockedScreen) + UpdateInactiveScreen(this); + + if (hasToBeResized || myLockedScreen) Resize(); if (myScreen != this && myScreen->isTabbable()) @@ -139,7 +148,11 @@ void PlaylistEditor::Update() MPD::SongList list; Mpd.GetPlaylistContent(locale_to_utf_cpy(Playlists->Current()), list); if (!list.empty()) - Content->SetTitle(Config.titles_visibility ? "Playlist's content (" + IntoStr(list.size()) + " item" + (list.size() == 1 ? ")" : "s)") : ""); + { + std::string title = Config.titles_visibility ? "Playlist's content (" + IntoStr(list.size()) + " item" + (list.size() == 1 ? ")" : "s)") : ""; + title.resize(Content->GetWidth()); + Content->SetTitle(title); + } else Content->SetTitle(Config.titles_visibility ? "Playlist's content" : ""); bool bold = 0; @@ -175,7 +188,7 @@ void PlaylistEditor::Update() } } -void PlaylistEditor::NextColumn() +bool PlaylistEditor::NextColumn() { if (w == Playlists) { @@ -183,10 +196,12 @@ void PlaylistEditor::NextColumn() w->Refresh(); w = Content; Content->HighlightColor(Config.active_column_color); + return true; } + return false; } -void PlaylistEditor::PrevColumn() +bool PlaylistEditor::PrevColumn() { if (w == Content) { @@ -194,7 +209,9 @@ void PlaylistEditor::PrevColumn() w->Refresh(); w = Playlists; Playlists->HighlightColor(Config.active_column_color); + return true; } + return false; } void PlaylistEditor::AddToPlaylist(bool add_n_play) diff --git a/src/playlist_editor.h b/src/playlist_editor.h index 3000e793..a9729994 100644 --- a/src/playlist_editor.h +++ b/src/playlist_editor.h @@ -52,18 +52,22 @@ class PlaylistEditor : public Screen virtual List *GetList(); - void NextColumn(); - void PrevColumn(); + virtual bool isMergable() { return true; } + + bool NextColumn(); + bool PrevColumn(); Menu *Playlists; Menu *Content; protected: virtual void Init(); + virtual bool isLockable() { return true; } private: void AddToPlaylist(bool); + static size_t LeftColumnStartX; static size_t LeftColumnWidth; static size_t RightColumnStartX; static size_t RightColumnWidth; diff --git a/src/screen.cpp b/src/screen.cpp new file mode 100644 index 00000000..b25f5a80 --- /dev/null +++ b/src/screen.cpp @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (C) 2008-2011 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 + +#include "screen.h" +#include "global.h" + +using Global::myScreen; +using Global::myLockedScreen; +using Global::myInactiveScreen; + +void ApplyToVisibleWindows(void (BasicScreen::*f)()) +{ + if (myLockedScreen) + { + if (myScreen == myLockedScreen) + { + if (myInactiveScreen) + (myInactiveScreen->*f)(); + } + else + (myLockedScreen->*f)(); + } + (myScreen->*f)(); +} + +void UpdateInactiveScreen(BasicScreen *screen) +{ + myInactiveScreen = myLockedScreen == screen ? 0 : myScreen; +} + +bool isVisible(BasicScreen *screen) +{ + assert(screen != 0); + if (myLockedScreen) + return screen == myScreen || screen == myInactiveScreen || screen == myLockedScreen; + else + return screen == myScreen; +} + +void BasicScreen::GetWindowResizeParams(size_t &x_offset, size_t &width, bool adjust_locked_screen) +{ + width = COLS; + x_offset = 0; + if (myLockedScreen && myInactiveScreen) + { + size_t locked_width = COLS*Config.locked_screen_width_part; + if (myLockedScreen == this) + width = locked_width; + else + { + width = COLS-locked_width-1; + x_offset = locked_width+1; + + if (adjust_locked_screen) + { + myLockedScreen->Resize(); + myLockedScreen->Refresh(); + + attron(COLOR_PAIR(Config.main_color)); + mvvline(Global::MainStartY, x_offset-1, 0, Global::MainHeight); + attroff(COLOR_PAIR(Config.main_color)); + refresh(); + } + } + } +} + +bool BasicScreen::Lock() +{ + if (myLockedScreen) + return false; + if (isLockable()) + { + myLockedScreen = this; + return true; + } + else + return false; +} + +void BasicScreen::Unlock() +{ + if (myInactiveScreen && myInactiveScreen != myLockedScreen) + myScreen = myInactiveScreen; + myLockedScreen->SwitchTo(); + myLockedScreen = 0; + myInactiveScreen = 0; +} diff --git a/src/screen.h b/src/screen.h index 48f906cd..8bdc13a7 100644 --- a/src/screen.h +++ b/src/screen.h @@ -28,6 +28,10 @@ #include "settings.h" #include "status.h" +void ApplyToVisibleWindows(void (BasicScreen::*f)()); +void UpdateInactiveScreen(BasicScreen *); +bool isVisible(BasicScreen *); + /// An interface for various instantiations of Screen template class. Since C++ doesn't like /// comparison of two different instantiations of the same template class we need the most /// basic class to be non-template to allow it. @@ -43,7 +47,7 @@ class BasicScreen /// @see Screen::ActiveWindow() /// - virtual void *ActiveWindow() = 0; + virtual Window *ActiveWindow() = 0; /// Method used for switching to screen /// @@ -129,10 +133,23 @@ class BasicScreen /// virtual bool isTabbable() { return false; } + /// @return true if screen is mergable, ie. can be "proper" subwindow + /// if one of the screens is locked. Screens that somehow resemble popups + /// will want to return false here. + virtual bool isMergable() = 0; + + /// Locks current screen. + /// @return true if lock was successful, false otherwise. Note that + /// if there is already locked screen, it'll not overwrite it. + bool Lock(); + /// Should be set to true each time screen needs resize /// bool hasToBeResized; + /// Unlocks a screen, ie. hides merged window (if there is one set). + static void Unlock(); + protected: /// Since screens initialization is lazy, we don't want to do /// this in the constructor. This function should be invoked @@ -141,6 +158,18 @@ class BasicScreen /// virtual void Init() = 0; + /// @return true if screen can be locked. Note that returning + /// false here doesn't neccesarily mean that screen is also not + /// mergable (eg. lyrics screen is not lockable since that wouldn't + /// make much sense, but it's perfectly fine to merge it). + virtual bool isLockable() = 0; + + /// Gets X offset and width of current screen to be used eg. in Resize() function. + /// @param adjust_locked_screen indicates whether this function should + /// automatically adjust locked screen's dimensions (is there is one set) + /// if current screen is going to be subwindow. + void GetWindowResizeParams(size_t &x_offset, size_t &width, bool adjust_locked_screen = true); + /// Flag that inditates whether the screen is initialized or not /// bool isInitialized; @@ -161,7 +190,7 @@ template class Screen : public BasicScreen /// active /// @return address to window object cast to void * /// - virtual void *ActiveWindow(); + virtual Window *ActiveWindow(); /// @return pointer to currently active window /// @@ -203,7 +232,7 @@ template class Screen : public BasicScreen WindowType *w; }; -template void *Screen::ActiveWindow() +template Window *Screen::ActiveWindow() { return w; } diff --git a/src/search_engine.cpp b/src/search_engine.cpp index 84e9e694..33c56bf9 100644 --- a/src/search_engine.cpp +++ b/src/search_engine.cpp @@ -78,15 +78,18 @@ void SearchEngine::Init() void SearchEngine::Resize() { - w->Resize(COLS, MainHeight); - w->MoveTo(0, MainStartY); - w->SetTitle(Config.columns_in_search_engine && Config.titles_visibility ? Display::Columns() : ""); + size_t x_offset, width; + GetWindowResizeParams(x_offset, width); + w->Resize(width, MainHeight); + w->MoveTo(x_offset, MainStartY); + w->SetTitle(Config.columns_in_search_engine && Config.titles_visibility ? Display::Columns(w->GetWidth()) : ""); hasToBeResized = 0; } void SearchEngine::SwitchTo() { using Global::myScreen; + using Global::myLockedScreen; if (myScreen == this) { @@ -97,7 +100,10 @@ void SearchEngine::SwitchTo() if (!isInitialized) Init(); - if (hasToBeResized) + if (myLockedScreen) + UpdateInactiveScreen(this); + + if (hasToBeResized || myLockedScreen) Resize(); if (w->Empty()) @@ -156,7 +162,7 @@ void SearchEngine::EnterPressed() if (!w->Back().first) { if (Config.columns_in_search_engine) - w->SetTitle(Config.titles_visibility ? Display::Columns() : ""); + w->SetTitle(Config.titles_visibility ? Display::Columns(w->GetWidth()) : ""); size_t found = w->Size()-SearchEngine::StaticOptions; found += 3; // don't count options inserted below w->InsertSeparator(ResetButton+1); diff --git a/src/search_engine.h b/src/search_engine.h index c5b3d88c..8a3d47ef 100644 --- a/src/search_engine.h +++ b/src/search_engine.h @@ -48,6 +48,8 @@ class SearchEngine : public Screen< Menu< std::pair > > virtual List *GetList() { return w->Size() >= StaticOptions ? w : 0; } + virtual bool isMergable() { return true; } + void UpdateFoundList(); void Scroll(int); void SelectAlbum(); @@ -58,6 +60,7 @@ class SearchEngine : public Screen< Menu< std::pair > > protected: virtual void Init(); + virtual bool isLockable() { return true; } private: void Prepare(); diff --git a/src/sel_items_adder.h b/src/sel_items_adder.h index 230c7c57..f6c91584 100644 --- a/src/sel_items_adder.h +++ b/src/sel_items_adder.h @@ -43,8 +43,11 @@ class SelectedItemsAdder : public Screen< Menu > virtual List *GetList() { return w; } + virtual bool isMergable() { return false; } + protected: virtual void Init(); + virtual bool isLockable() { return false; } private: void SetDimensions(); diff --git a/src/server_info.h b/src/server_info.h index 07d4ed6f..18b3b368 100644 --- a/src/server_info.h +++ b/src/server_info.h @@ -40,8 +40,11 @@ class ServerInfo : public Screen virtual List *GetList() { return 0; } + virtual bool isMergable() { return false; } + protected: virtual void Init(); + virtual bool isLockable() { return false; } private: void SetDimensions(); diff --git a/src/settings.cpp b/src/settings.cpp index b47a3793..9f53b24b 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -252,6 +252,7 @@ void NcmpcppKeys::SetDefaults() ToggleSeparatorsInPlaylist[0] = '!'; ToggleLyricsDB[0] = 'L'; ToggleFetchingLyricsInBackground[0] = 'F'; + ToggleScreenLock[0] = 12; GoToParentDir[0] = KEY_BACKSPACE; SwitchTagTypeList[0] = '`'; Quit[0] = 'q'; @@ -341,6 +342,7 @@ void NcmpcppKeys::SetDefaults() ToggleSeparatorsInPlaylist[1] = NullKey; ToggleLyricsDB[1] = NullKey; ToggleFetchingLyricsInBackground[1] = NullKey; + ToggleScreenLock[1] = NullKey; GoToParentDir[1] = 127; SwitchTagTypeList[1] = NullKey; Quit[1] = 'Q'; @@ -446,6 +448,7 @@ void NcmpcppConfig::SetDefaults() lines_scrolled = 2; search_engine_default_search_mode = 0; visualizer_sync_interval = 30; + locked_screen_width_part = 0.5; selected_item_suffix_length = 0; now_playing_suffix_length = 0; # ifdef HAVE_LANGINFO_H @@ -1131,6 +1134,12 @@ void NcmpcppConfig::Read() if (interval) visualizer_sync_interval = interval; } + else if (cl.find("locked_screen_width_part") != std::string::npos) + { + unsigned part = StrToInt(v); + if (part) + locked_screen_width_part = part/100.0; + } else if (cl.find("song_window_title_format") != std::string::npos) { if (!v.empty() && MPD::Song::isFormatOk("song_window_title_format", v)) diff --git a/src/settings.h b/src/settings.h index 75f88177..5344e230 100644 --- a/src/settings.h +++ b/src/settings.h @@ -143,6 +143,7 @@ struct NcmpcppKeys int ToggleSeparatorsInPlaylist[2]; int ToggleLyricsDB[2]; int ToggleFetchingLyricsInBackground[2]; + int ToggleScreenLock[2]; int GoToParentDir[2]; int SwitchTagTypeList[2]; int Quit[2]; @@ -270,6 +271,8 @@ struct NcmpcppConfig unsigned search_engine_default_search_mode; unsigned visualizer_sync_interval; + double locked_screen_width_part; + size_t selected_item_suffix_length; size_t now_playing_suffix_length; diff --git a/src/song_info.cpp b/src/song_info.cpp index 8eb3e935..1d86e791 100644 --- a/src/song_info.cpp +++ b/src/song_info.cpp @@ -24,8 +24,6 @@ using Global::MainHeight; using Global::MainStartY; -using Global::myScreen; -using Global::myOldScreen; SongInfo *mySongInfo = new SongInfo; @@ -53,8 +51,10 @@ void SongInfo::Init() void SongInfo::Resize() { - w->Resize(COLS, MainHeight); - w->MoveTo(0, MainStartY); + size_t x_offset, width; + GetWindowResizeParams(x_offset, width); + w->Resize(width, MainHeight); + w->MoveTo(x_offset, MainStartY); hasToBeResized = 0; } @@ -65,18 +65,25 @@ std::basic_string SongInfo::Title() void SongInfo::SwitchTo() { + using Global::myScreen; + using Global::myOldScreen; + using Global::myLockedScreen; + if (myScreen == this) return myOldScreen->SwitchTo(); if (!isInitialized) Init(); + if (myLockedScreen) + UpdateInactiveScreen(this); + MPD::Song *s = myScreen->CurrentSong(); if (!s) return; - if (hasToBeResized) + if (hasToBeResized || myLockedScreen) Resize(); myOldScreen = myScreen; diff --git a/src/song_info.h b/src/song_info.h index 2c126739..d24c380b 100644 --- a/src/song_info.h +++ b/src/song_info.h @@ -46,10 +46,13 @@ class SongInfo : public Screen virtual List *GetList() { return 0; } + virtual bool isMergable() { return true; } + static const Metadata Tags[]; protected: virtual void Init(); + virtual bool isLockable() { return false; } private: void PrepareSong(MPD::Song &); diff --git a/src/status.cpp b/src/status.cpp index 33255ce6..a6834acb 100644 --- a/src/status.cpp +++ b/src/status.cpp @@ -41,6 +41,8 @@ #include "visualizer.h" using Global::myScreen; +using Global::myLockedScreen; +using Global::myInactiveScreen; using Global::wFooter; using Global::Timer; using Global::wHeader; @@ -156,6 +158,16 @@ void TraceMpdStatus() Global::UpdateStatusImmediately = 0; } + if (myLockedScreen) + { + if (myScreen == myLockedScreen) + { + if (myInactiveScreen) + myInactiveScreen->Update(); + } + else + myLockedScreen->Update(); + } myScreen->Update(); if (myScreen->ActiveWindow() == myPlaylist->Items @@ -283,19 +295,19 @@ void NcmpcppStatusChanged(MPD::Connection *, MPD::StatusChanges changed, void *) if (!Global::BlockItemListUpdate) { - if (myScreen == myBrowser) + if (isVisible(myBrowser)) { myBrowser->UpdateItemList(); } - else if (myScreen == mySearcher) + else if (isVisible(mySearcher)) { mySearcher->UpdateFoundList(); } - else if (myScreen == myLibrary) + else if (isVisible(myLibrary)) { UpdateSongList(myLibrary->Songs); } - else if (myScreen == myPlaylistEditor) + else if (isVisible(myPlaylistEditor)) { UpdateSongList(myPlaylistEditor->Content); } @@ -305,7 +317,7 @@ void NcmpcppStatusChanged(MPD::Connection *, MPD::StatusChanges changed, void *) { if (myBrowser->Main()) { - if (myScreen == myBrowser) + if (isVisible(myBrowser)) myBrowser->GetDirectory(myBrowser->CurrentDir()); else myBrowser->Main()->Clear(); @@ -370,7 +382,7 @@ void NcmpcppStatusChanged(MPD::Connection *, MPD::StatusChanges changed, void *) else player_state.clear(); # ifdef ENABLE_VISUALIZER - if (myScreen == myVisualizer) + if (isVisible(myVisualizer)) myVisualizer->Main()->Clear(); # endif // ENABLE_VISUALIZER break; @@ -412,7 +424,7 @@ void NcmpcppStatusChanged(MPD::Connection *, MPD::StatusChanges changed, void *) if (Config.autocenter_mode && !myPlaylist->Items->isFiltered()) myPlaylist->Items->Highlight(myPlaylist->NowPlaying); - if (Config.now_playing_lyrics && myScreen == myLyrics && Global::myOldScreen == myPlaylist) + if (Config.now_playing_lyrics && isVisible(myLyrics) && Global::myOldScreen == myPlaylist) myLyrics->ReloadNP = 1; } Playlist::ReloadRemaining = 1; @@ -647,7 +659,7 @@ void NcmpcppStatusChanged(MPD::Connection *, MPD::StatusChanges changed, void *) if (changed.PlayerState || (changed.ElapsedTime && (!Config.new_design || Mpd.GetState() == MPD::psPlay))) wFooter->Refresh(); if (changed.Playlist || changed.Database || changed.PlayerState || changed.SongID) - myScreen->RefreshWindow(); + ApplyToVisibleWindows(&BasicScreen::RefreshWindow); } Window &Statusbar() diff --git a/src/tag_editor.cpp b/src/tag_editor.cpp index 8dffed8e..c6f83a68 100644 --- a/src/tag_editor.cpp +++ b/src/tag_editor.cpp @@ -48,6 +48,7 @@ const std::string TagEditor::PatternsFile = config_dir + "patterns.list"; std::list TagEditor::Patterns; size_t TagEditor::LeftColumnWidth; +size_t TagEditor::LeftColumnStartX; size_t TagEditor::MiddleColumnWidth; size_t TagEditor::MiddleColumnStartX; size_t TagEditor::RightColumnWidth; @@ -62,7 +63,7 @@ size_t TagEditor::FParserHeight; void TagEditor::Init() { - SetDimensions(); + SetDimensions(0, COLS); Albums = new Menu(0, MainStartY, LeftColumnWidth, MainHeight, Config.titles_visibility ? "Albums" : "", Config.main_color, brNone); Albums->HighlightColor(Config.active_column_color); @@ -133,17 +134,18 @@ void TagEditor::Init() isInitialized = 1; } -void TagEditor::SetDimensions() +void TagEditor::SetDimensions(size_t x_offset, size_t width) { MiddleColumnWidth = std::min(26, COLS-2); - LeftColumnWidth = (COLS-MiddleColumnWidth)/2; - MiddleColumnStartX = LeftColumnWidth+1; - RightColumnWidth = COLS-LeftColumnWidth-MiddleColumnWidth-2; - RightColumnStartX = LeftColumnWidth+MiddleColumnWidth+2; + LeftColumnStartX = x_offset; + LeftColumnWidth = (width-MiddleColumnWidth)/2; + MiddleColumnStartX = LeftColumnStartX+LeftColumnWidth+1; + RightColumnWidth = width-LeftColumnWidth-MiddleColumnWidth-2; + RightColumnStartX = MiddleColumnStartX+MiddleColumnWidth+1; FParserDialogWidth = std::min(30, COLS); FParserDialogHeight = std::min(size_t(6), MainHeight); - FParserWidth = COLS*0.9; + FParserWidth = width*0.9; FParserHeight = std::min(size_t(LINES*0.8), MainHeight); FParserWidthOne = FParserWidth/2; FParserWidthTwo = FParserWidth-FParserWidthOne; @@ -151,7 +153,9 @@ void TagEditor::SetDimensions() void TagEditor::Resize() { - SetDimensions(); + size_t x_offset, width; + GetWindowResizeParams(x_offset, width); + SetDimensions(x_offset, width); Albums->Resize(LeftColumnWidth, MainHeight); Dirs->Resize(LeftColumnWidth, MainHeight); @@ -162,15 +166,15 @@ void TagEditor::Resize() FParserLegend->Resize(FParserWidthTwo, FParserHeight); FParserPreview->Resize(FParserWidthTwo, FParserHeight); - Albums->MoveTo(0, MainStartY); - Dirs->MoveTo(0, MainStartY); + Albums->MoveTo(LeftColumnStartX, MainStartY); + Dirs->MoveTo(LeftColumnStartX, MainStartY); TagTypes->MoveTo(MiddleColumnStartX, MainStartY); Tags->MoveTo(RightColumnStartX, MainStartY); - FParserDialog->MoveTo((COLS-FParserDialogWidth)/2, (MainHeight-FParserDialogHeight)/2+MainStartY); - FParser->MoveTo((COLS-FParserWidth)/2, (MainHeight-FParserHeight)/2+MainStartY); - FParserLegend->MoveTo((COLS-FParserWidth)/2+FParserWidthOne, (MainHeight-FParserHeight)/2+MainStartY); - FParserPreview->MoveTo((COLS-FParserWidth)/2+FParserWidthOne, (MainHeight-FParserHeight)/2+MainStartY); + FParserDialog->MoveTo(x_offset+(width-FParserDialogWidth)/2, (MainHeight-FParserDialogHeight)/2+MainStartY); + FParser->MoveTo(x_offset+(width-FParserWidth)/2, (MainHeight-FParserHeight)/2+MainStartY); + FParserLegend->MoveTo(x_offset+(width-FParserWidth)/2+FParserWidthOne, (MainHeight-FParserHeight)/2+MainStartY); + FParserPreview->MoveTo(x_offset+(width-FParserWidth)/2+FParserWidthOne, (MainHeight-FParserHeight)/2+MainStartY); if (MainHeight < 5 && (w == FParserDialog || w == FParser || w == FParserHelper)) // screen too low w = TagTypes; // fall back to main columns @@ -186,6 +190,7 @@ std::basic_string TagEditor::Title() void TagEditor::SwitchTo() { using Global::myScreen; + using Global::myLockedScreen; if (myScreen == this) return; @@ -193,7 +198,10 @@ void TagEditor::SwitchTo() if (!isInitialized) Init(); - if (hasToBeResized) + if (myLockedScreen) + UpdateInactiveScreen(this); + + if (hasToBeResized || myLockedScreen) Resize(); if (myScreen != this && myScreen->isTabbable()) @@ -800,7 +808,7 @@ List *TagEditor::GetList() return 0; } -void TagEditor::NextColumn() +bool TagEditor::NextColumn() { if (w == LeftColumn) { @@ -808,6 +816,7 @@ void TagEditor::NextColumn() w->Refresh(); w = TagTypes; TagTypes->HighlightColor(Config.active_column_color); + return true; } else if (w == TagTypes && TagTypes->Choice() < 12 && !Tags->ReallyEmpty()) { @@ -815,6 +824,7 @@ void TagEditor::NextColumn() w->Refresh(); w = Tags; Tags->HighlightColor(Config.active_column_color); + return true; } else if (w == FParser) { @@ -823,10 +833,12 @@ void TagEditor::NextColumn() w = FParserHelper; FParserHelper->SetBorder(Config.active_window_border); FParserHelper->Display(); + return true; } + return false; } -void TagEditor::PrevColumn() +bool TagEditor::PrevColumn() { if (w == Tags) { @@ -834,6 +846,7 @@ void TagEditor::PrevColumn() w->Refresh(); w = TagTypes; TagTypes->HighlightColor(Config.active_column_color); + return true; } else if (w == TagTypes) { @@ -841,6 +854,7 @@ void TagEditor::PrevColumn() w->Refresh(); w = LeftColumn; LeftColumn->HighlightColor(Config.active_column_color); + return true; } else if (w == FParserHelper) { @@ -849,7 +863,9 @@ void TagEditor::PrevColumn() w = FParser; FParser->SetBorder(Config.active_window_border); FParser->Display(); + return true; } + return false; } void TagEditor::LocateSong(const MPD::Song &s) diff --git a/src/tag_editor.h b/src/tag_editor.h index a2858242..06d335bd 100644 --- a/src/tag_editor.h +++ b/src/tag_editor.h @@ -66,8 +66,10 @@ class TagEditor : public Screen virtual List *GetList(); - void NextColumn(); - void PrevColumn(); + virtual bool isMergable() { return true; } + + bool NextColumn(); + bool PrevColumn(); void LocateSong(const MPD::Song &s); @@ -85,9 +87,10 @@ class TagEditor : public Screen protected: virtual void Init(); + virtual bool isLockable() { return true; } private: - void SetDimensions(); + void SetDimensions(size_t, size_t); MPD::SongList EditedSongs; Menu *FParserDialog; @@ -118,6 +121,7 @@ class TagEditor : public Screen static std::list Patterns; static size_t MiddleColumnWidth; + static size_t LeftColumnStartX; static size_t LeftColumnWidth; static size_t MiddleColumnStartX; static size_t RightColumnWidth; diff --git a/src/tiny_tag_editor.cpp b/src/tiny_tag_editor.cpp index 2860bba7..d336cf66 100644 --- a/src/tiny_tag_editor.cpp +++ b/src/tiny_tag_editor.cpp @@ -54,23 +54,32 @@ void TinyTagEditor::Init() void TinyTagEditor::Resize() { - w->Resize(COLS, MainHeight); - w->MoveTo(0, MainStartY); + size_t x_offset, width; + GetWindowResizeParams(x_offset, width); + w->Resize(width, MainHeight); + w->MoveTo(x_offset, MainStartY); hasToBeResized = 0; } void TinyTagEditor::SwitchTo() { + using Global::myScreen; + using Global::myLockedScreen; + if (itsEdited.isStream()) { ShowMessage("Streams cannot be edited!"); } else if (GetTags()) { - if (hasToBeResized) + if (myLockedScreen) + UpdateInactiveScreen(this); + + if (hasToBeResized || myLockedScreen) Resize(); - myOldScreen = Global::myScreen; - Global::myScreen = this; + + myOldScreen = myScreen; + myScreen = this; Global::RedrawHeader = 1; } else diff --git a/src/tiny_tag_editor.h b/src/tiny_tag_editor.h index ba8a0ba1..4b455d6b 100644 --- a/src/tiny_tag_editor.h +++ b/src/tiny_tag_editor.h @@ -48,10 +48,13 @@ class TinyTagEditor : public Screen< Menu > virtual List *GetList() { return 0; } + virtual bool isMergable() { return true; } + bool SetEdited(MPD::Song *); protected: virtual void Init(); + virtual bool isLockable() { return true; } private: bool GetTags(); diff --git a/src/visualizer.cpp b/src/visualizer.cpp index 45089fb3..e8903d11 100644 --- a/src/visualizer.cpp +++ b/src/visualizer.cpp @@ -61,6 +61,7 @@ void Visualizer::Init() void Visualizer::SwitchTo() { using Global::myScreen; + using Global::myLockedScreen; if (myScreen == this) return; @@ -68,7 +69,10 @@ void Visualizer::SwitchTo() if (!isInitialized) Init(); - if (hasToBeResized) + if (myLockedScreen) + UpdateInactiveScreen(this); + + if (hasToBeResized || myLockedScreen) Resize(); if (myScreen != this && myScreen->isTabbable()) @@ -88,8 +92,10 @@ void Visualizer::SwitchTo() void Visualizer::Resize() { - w->Resize(COLS, MainHeight); - w->MoveTo(0, MainStartY); + size_t x_offset, width; + GetWindowResizeParams(x_offset, width); + w->Resize(width, MainHeight); + w->MoveTo(x_offset, MainStartY); hasToBeResized = 0; } @@ -154,11 +160,12 @@ void Visualizer::SpacePressed() void Visualizer::DrawSoundWave(int16_t *buf, ssize_t samples, size_t y_offset, size_t height) { - const int samples_per_col = samples/COLS; + const int samples_per_col = samples/w->GetWidth(); const int half_height = height/2; *w << fmtAltCharset; double prev_point_pos = 0; - for (int i = 0; i < COLS; ++i) + const size_t win_width = w->GetWidth(); + for (size_t i = 0; i < win_width; ++i) { double point_pos = 0; for (int j = 0; j < samples_per_col; ++j) @@ -198,8 +205,9 @@ void Visualizer::DrawFrequencySpectrum(int16_t *buf, ssize_t samples, size_t y_o for (unsigned i = 0; i < itsFFTResults; ++i) itsFreqsMagnitude[i] = sqrt(itsOutput[i][0]*itsOutput[i][0] + itsOutput[i][1]*itsOutput[i][1])/1e5*height/5; - const int freqs_per_col = itsFFTResults/COLS /* cut bandwidth a little to achieve better look */ * 4/5; - for (int i = 0; i < COLS; ++i) + const size_t win_width = w->GetWidth(); + const int freqs_per_col = itsFFTResults/win_width /* cut bandwidth a little to achieve better look */ * 7/10; + for (size_t i = 0; i < win_width; ++i) { size_t bar_height = 0; for (int j = 0; j < freqs_per_col; ++j) diff --git a/src/visualizer.h b/src/visualizer.h index d2c19f3c..15be5475 100644 --- a/src/visualizer.h +++ b/src/visualizer.h @@ -54,6 +54,8 @@ class Visualizer : public Screen virtual bool allowsSelection() { return false; } + virtual bool isMergable() { return true; } + void SetFD(); void ResetFD(); void FindOutputID(); @@ -62,6 +64,7 @@ class Visualizer : public Screen protected: virtual void Init(); + virtual bool isLockable() { return true; } private: void DrawSoundWave(int16_t *, ssize_t, size_t, size_t);