diff --git a/src/musikcube/CMakeLists.txt b/src/musikcube/CMakeLists.txt index d24c17840..a3aa2d257 100644 --- a/src/musikcube/CMakeLists.txt +++ b/src/musikcube/CMakeLists.txt @@ -30,6 +30,7 @@ set (CUBE_SRCS ./app/util/ConsoleLogger.cpp ./app/util/GlobalHotkeys.cpp ./app/util/Hotkeys.cpp + ./app/util/MagicConstants.cpp ./app/util/PreferenceKeys.cpp ./app/util/Playback.cpp ./app/util/Rating.cpp diff --git a/src/musikcube/app/layout/LibraryLayout.cpp b/src/musikcube/app/layout/LibraryLayout.cpp index 738fcead5..b6ba8ea64 100755 --- a/src/musikcube/app/layout/LibraryLayout.cpp +++ b/src/musikcube/app/layout/LibraryLayout.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include "LibraryLayout.h" @@ -392,6 +393,14 @@ void LibraryLayout::ProcessMessage(musik::core::runtime::IMessage &message) { LayoutBase::ProcessMessage(message); } +void LibraryLayout::ShowDirectoryChooser() { + BrowseOverlays::ShowDirectoryChooser( + this->library, + [this](std::string directory) { + this->ShowDirectories(directory); + }); +} + bool LibraryLayout::KeyPress(const std::string& key) { if (this->visibleLayout == this->browseLayout || this->visibleLayout == this->directoryLayout) @@ -419,8 +428,13 @@ bool LibraryLayout::KeyPress(const std::string& key) { else if (Hotkeys::Is(Hotkeys::NavigateLibraryBrowseChooseCategory, key)) { BrowseOverlays::ShowCategoryChooser( this->library, - [this](std::string category) { - this->ShowBrowse(category); + [this](std::string category, std::string type) { + if (type == MagicConstants::DirectoryCategoryType) { + this->ShowDirectoryChooser(); + } + else { + this->ShowBrowse(category); + } }); return true; } @@ -443,11 +457,7 @@ bool LibraryLayout::KeyPress(const std::string& key) { return true; } else if (Hotkeys::Is(Hotkeys::NavigateLibraryBrowseDirectories, key)) { - BrowseOverlays::ShowDirectoryChooser( - this->library, - [this](std::string directory) { - this->ShowDirectories(directory); - }); + this->ShowDirectoryChooser(); return true; } else if (this->GetFocus() == this->transportView && Hotkeys::Is(Hotkeys::Up, key)) { diff --git a/src/musikcube/app/layout/LibraryLayout.h b/src/musikcube/app/layout/LibraryLayout.h index 27aa9921d..9f330142b 100755 --- a/src/musikcube/app/layout/LibraryLayout.h +++ b/src/musikcube/app/layout/LibraryLayout.h @@ -96,6 +96,7 @@ namespace musik { void ShowBrowse(const std::string& category = ""); void ShowCategorySearch(); void ShowTrackSearch(); + void ShowDirectoryChooser(); void ShowDirectories(const std::string& directory); void ChangeMainLayout(std::shared_ptr newLayout); diff --git a/src/musikcube/app/overlay/BrowseOverlays.cpp b/src/musikcube/app/overlay/BrowseOverlays.cpp index 27c7cfc1c..85a49b2cb 100644 --- a/src/musikcube/app/overlay/BrowseOverlays.cpp +++ b/src/musikcube/app/overlay/BrowseOverlays.cpp @@ -35,7 +35,9 @@ #include #include "BrowseOverlays.h" +#include #include +#include #include #include #include @@ -48,7 +50,7 @@ using namespace musik::core; using namespace musik::core::db; using namespace musik::core::db::local; -static const std::set BLACKLIST = { "bitrate", "channels", "lyrics", "path_id" }; +static const std::set BLACKLIST = { "bitrate", "channels", "lyrics", "path_id", "directory" }; static std::string lastSelectedCategory, lastSelectedDirectory; static void showNoPathsError() { @@ -64,7 +66,7 @@ static void showNoPathsError() { void BrowseOverlays::ShowCategoryChooser( musik::core::ILibraryPtr library, - std::function callback) + std::function callback) { using Adapter = cursespp::SimpleScrollAdapter; using ListOverlay = cursespp::ListOverlay; @@ -77,16 +79,29 @@ void BrowseOverlays::ShowCategoryChooser( adapter->SetSelectable(true); size_t index = 0; - auto filtered = query->GetResult()->Filter([&index, adapter](const Value& value) -> bool { + + auto result = query->GetResult(); + + auto filtered = result->Filter([&index, adapter](const Value& value) -> bool { auto str = value->ToString(); - if (BLACKLIST.find(str) == BLACKLIST.end()) { - adapter->AddEntry(str); - if (lastSelectedCategory == str) { - index = adapter->GetEntryCount() - 1; - } - return true; + return BLACKLIST.find(str) == BLACKLIST.end(); + }); + + filtered->Add(std::make_shared( + _TSTR("browse_title_directory"), + -1LL, + MagicConstants::DirectoryCategoryType)); + + filtered->Sort([](const auto a, const auto b) -> bool { + return a->ToString() < b->ToString(); + }); + + filtered->Each([&index, adapter](const Value& value) { + auto str = value->ToString(); + adapter->AddEntry(str); + if (lastSelectedCategory == str) { + index = adapter->GetEntryCount() - 1; } - return false; }); std::shared_ptr dialog(new ListOverlay()); @@ -98,8 +113,9 @@ void BrowseOverlays::ShowCategoryChooser( .SetItemSelectedCallback( [callback, filtered] (ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) { - lastSelectedCategory = filtered->At(index)->ToString(); - callback(lastSelectedCategory); + auto selected = filtered->At(index); + lastSelectedCategory = selected->ToString(); + callback(lastSelectedCategory, selected->GetType()); }); cursespp::App::Overlays().Push(dialog); diff --git a/src/musikcube/app/overlay/BrowseOverlays.h b/src/musikcube/app/overlay/BrowseOverlays.h index 6446ccea6..cf2ca7720 100644 --- a/src/musikcube/app/overlay/BrowseOverlays.h +++ b/src/musikcube/app/overlay/BrowseOverlays.h @@ -43,7 +43,7 @@ namespace musik { public: static void ShowCategoryChooser( musik::core::ILibraryPtr library, - std::function callback); + std::function callback); static void ShowDirectoryChooser( musik::core::ILibraryPtr library, diff --git a/src/musikcube/app/overlay/PlayQueueOverlays.cpp b/src/musikcube/app/overlay/PlayQueueOverlays.cpp index c392fef4f..d2ca78840 100644 --- a/src/musikcube/app/overlay/PlayQueueOverlays.cpp +++ b/src/musikcube/app/overlay/PlayQueueOverlays.cpp @@ -50,6 +50,7 @@ #include #include +#include #include #include @@ -77,8 +78,6 @@ static size_t lastCategoryOverlayIndex = 0; static int64_t lastPlaylistId = -1; static milliseconds lastOperationExpiry; -static std::string kDirectoryFieldColumn = "__directoryCategory__"; - using Adapter = cursespp::SimpleScrollAdapter; static inline milliseconds now() { @@ -132,7 +131,7 @@ static std::shared_ptr queryTracksByCategory( special `fieldColumn` called `kDirectoryFieldColumn`. If we detect this case we'll run a special query for directory path matching */ std::shared_ptr query; - if (categoryType == kDirectoryFieldColumn) { + if (categoryType == MagicConstants::DirectoryCategoryType) { query = std::make_shared(library, categoryValue); } else { @@ -558,7 +557,13 @@ void PlayQueueOverlays::ShowAddDirectoryOverlay( musik::core::ILibraryPtr library, const std::string& directory) { - ShowAddCategoryOverlay(messageQueue, playback, library, kDirectoryFieldColumn, directory, -1LL); + ShowAddCategoryOverlay( + messageQueue, + playback, + library, + MagicConstants::DirectoryCategoryType, + directory, + -1LL); } void PlayQueueOverlays::ShowAlbumDividerOverlay( diff --git a/src/musikcube/app/util/MagicConstants.cpp b/src/musikcube/app/util/MagicConstants.cpp new file mode 100644 index 000000000..cbff9c3ca --- /dev/null +++ b/src/musikcube/app/util/MagicConstants.cpp @@ -0,0 +1,43 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2004-2019 musikcube team +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the author nor the names of other contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "MagicConstants.h" + +using namespace musik::cube; + +namespace musik { namespace cube { + + const std::string MagicConstants::DirectoryCategoryType = "__DirectoryCategoryType__"; + +} } diff --git a/src/musikcube/app/util/MagicConstants.h b/src/musikcube/app/util/MagicConstants.h new file mode 100644 index 000000000..f027a3e83 --- /dev/null +++ b/src/musikcube/app/util/MagicConstants.h @@ -0,0 +1,55 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2004-2019 musikcube team +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the author nor the names of other contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include + +namespace musik { namespace cube { + + /** + * MagicConstants is used to house shared constants that are typically used as markers + * to glue together otherwise disjoint features that may make sense to interoperate in + * and integrate in the UI layer. One example of this: present directory browsing as + * a category type, even though it's a category in the database. + * + * We should strive for magic constants to be empty, but in some cases doing serious + * refactors of subsystems is prohibitively expensive and would not gain much. + */ + struct MagicConstants { + static const std::string DirectoryCategoryType; + private: MagicConstants(){} + }; + +} }