diff --git a/map/bookmark.cpp b/map/bookmark.cpp index 48c5bf9542..4a777073bd 100644 --- a/map/bookmark.cpp +++ b/map/bookmark.cpp @@ -138,8 +138,9 @@ Track const * BookmarkCategory::GetTrack(size_t index) const return (index < m_tracks.size() ? m_tracks[index].get() : 0); } -BookmarkCategory::BookmarkCategory(std::string const & name) - : Base(0.0 /* bookmarkDepth */, UserMark::Type::BOOKMARK) +BookmarkCategory::BookmarkCategory(std::string const & name, + Listeners const & listeners) + : Base(0.0 /* bookmarkDepth */, UserMark::Type::BOOKMARK, listeners) , m_name(name) {} @@ -587,9 +588,10 @@ bool BookmarkCategory::LoadFromKML(ReaderPtr const & reader) } // static -std::unique_ptr BookmarkCategory::CreateFromKMLFile(std::string const & file) +std::unique_ptr BookmarkCategory::CreateFromKMLFile(std::string const & file, + Listeners const & listeners) { - auto cat = my::make_unique(""); + auto cat = my::make_unique("", listeners); try { if (cat->LoadFromKML(my::make_unique(file))) diff --git a/map/bookmark.hpp b/map/bookmark.hpp index 5e46c03fdd..21c699ae7b 100644 --- a/map/bookmark.hpp +++ b/map/bookmark.hpp @@ -111,7 +111,8 @@ class BookmarkCategory : public UserMarkContainer { using Base = UserMarkContainer; public: - explicit BookmarkCategory(std::string const & name); + explicit BookmarkCategory(std::string const & name, + Listeners const & listeners); ~BookmarkCategory() override; size_t GetUserLineCount() const override; @@ -144,7 +145,8 @@ public: bool SaveToKMLFile(); /// @return nullptr in the case of error - static std::unique_ptr CreateFromKMLFile(std::string const & file); + static std::unique_ptr CreateFromKMLFile(std::string const & file, + Listeners const & listeners); /// Get valid file name from input (remove illegal symbols). static std::string RemoveInvalidSymbols(std::string const & name); diff --git a/map/bookmark_manager.cpp b/map/bookmark_manager.cpp index edf91bf9d1..261f59d0e0 100644 --- a/map/bookmark_manager.cpp +++ b/map/bookmark_manager.cpp @@ -64,8 +64,20 @@ std::string const GenerateValidAndUniqueFilePathForKML(std::string const & fileN } } // namespace -BookmarkManager::BookmarkManager(GetStringsBundleFn && getStringsBundleFn) +BookmarkManager::BookmarkManager(GetStringsBundleFn && getStringsBundleFn, + CreatedBookmarksCallback && createdBookmarksCallback, + UpdatedBookmarksCallback && updatedBookmarksCallback, + DeletedBookmarksCallback && deletedBookmarksCallback) : m_getStringsBundle(std::move(getStringsBundleFn)) + , m_createdBookmarksCallback(std::move(createdBookmarksCallback)) + , m_updatedBookmarksCallback(std::move(updatedBookmarksCallback)) + , m_deletedBookmarksCallback(std::move(deletedBookmarksCallback)) + , m_bookmarksListeners(std::bind(&BookmarkManager::OnCreateUserMarks, this, + std::placeholders::_1, std::placeholders::_2), + std::bind(&BookmarkManager::OnUpdateUserMarks, this, + std::placeholders::_1, std::placeholders::_2), + std::bind(&BookmarkManager::OnDeleteUserMarks, this, + std::placeholders::_1, std::placeholders::_2)) , m_needTeardown(false) { ASSERT(m_getStringsBundle != nullptr, ()); @@ -149,7 +161,7 @@ void BookmarkManager::LoadBookmarks() auto collection = std::make_shared(); for (auto const & file : files) { - auto cat = BookmarkCategory::CreateFromKMLFile(dir + file); + auto cat = BookmarkCategory::CreateFromKMLFile(dir + file, m_bookmarksListeners); if (m_needTeardown) return; @@ -189,7 +201,7 @@ void BookmarkManager::LoadBookmarkRoutine(std::string const & filePath, bool isT } else { - auto cat = BookmarkCategory::CreateFromKMLFile(fileSavePath.get()); + auto cat = BookmarkCategory::CreateFromKMLFile(fileSavePath.get(), m_bookmarksListeners); if (m_needTeardown) return; @@ -385,9 +397,64 @@ BookmarkCategory * BookmarkManager::GetBmCategory(size_t index) const return (index < m_categories.size() ? m_categories[index].get() : 0); } +void BookmarkManager::OnCreateUserMarks(UserMarkContainer * container, df::IDCollection const & markIds) +{ + if (container->GetType() != UserMark::Type::BOOKMARK) + return; + + if (m_createdBookmarksCallback == nullptr) + return; + + std::vector> marksInfo; + GetBookmarksData(container, markIds, marksInfo); + + m_createdBookmarksCallback(marksInfo); +} + +void BookmarkManager::OnUpdateUserMarks(UserMarkContainer * container, df::IDCollection const & markIds) +{ + if (container->GetType() != UserMark::Type::BOOKMARK) + return; + + if (m_updatedBookmarksCallback == nullptr) + return; + + std::vector> marksInfo; + GetBookmarksData(container, markIds, marksInfo); + + m_updatedBookmarksCallback(marksInfo); +} + +void BookmarkManager::OnDeleteUserMarks(UserMarkContainer * container, df::IDCollection const & markIds) +{ + if (container->GetType() != UserMark::Type::BOOKMARK) + return; + + if (m_deletedBookmarksCallback == nullptr) + return; + + m_deletedBookmarksCallback(markIds); +} + +void BookmarkManager::GetBookmarksData(UserMarkContainer * container, df::IDCollection const & markIds, + std::vector> & data) const +{ + data.reserve(markIds.size()); + for (auto markId : markIds) + { + auto const userMark = container->GetUserMarkById(markId); + ASSERT(userMark != nullptr, ()); + ASSERT(dynamic_cast(userMark) != nullptr, ()); + + auto const bookmark = static_cast(userMark); + data.push_back(std::make_pair(markId, bookmark->GetData())); + } +} + size_t BookmarkManager::CreateBmCategory(std::string const & name) { - m_categories.emplace_back(new BookmarkCategory(name)); + m_categories.emplace_back(new BookmarkCategory(name, m_bookmarksListeners)); + df::DrapeEngineLockGuard lock(m_drapeEngine); if (lock) m_categories.back()->SetDrapeEngine(lock.Get()); diff --git a/map/bookmark_manager.hpp b/map/bookmark_manager.hpp index ef3c9aa853..f97c01d252 100644 --- a/map/bookmark_manager.hpp +++ b/map/bookmark_manager.hpp @@ -32,6 +32,9 @@ public: using AsyncLoadingStartedCallback = std::function; using AsyncLoadingFinishedCallback = std::function; using AsyncLoadingFileCallback = std::function; + using CreatedBookmarksCallback = std::function> const &)>; + using UpdatedBookmarksCallback = std::function> const &)>; + using DeletedBookmarksCallback = std::function const &)>; struct AsyncLoadingCallbacks { @@ -41,7 +44,10 @@ public: AsyncLoadingFileCallback m_onFileSuccess; }; - explicit BookmarkManager(GetStringsBundleFn && getStringsBundleFn); + explicit BookmarkManager(GetStringsBundleFn && getStringsBundleFn, + CreatedBookmarksCallback && createdBookmarksCallback, + UpdatedBookmarksCallback && updatedBookmarksCallback, + DeletedBookmarksCallback && deletedBookmarksCallback); ~BookmarkManager(); void SetDrapeEngine(ref_ptr engine); @@ -107,7 +113,18 @@ private: void NotifyAboutFile(bool success, std::string const & filePath, bool isTemporaryFile); void LoadBookmarkRoutine(std::string const & filePath, bool isTemporaryFile); + void OnCreateUserMarks(UserMarkContainer * container, df::IDCollection const & markIds); + void OnUpdateUserMarks(UserMarkContainer * container, df::IDCollection const & markIds); + void OnDeleteUserMarks(UserMarkContainer * container, df::IDCollection const & markIds); + void GetBookmarksData(UserMarkContainer * container, df::IDCollection const & markIds, + std::vector> & data) const; + GetStringsBundleFn m_getStringsBundle; + CreatedBookmarksCallback m_createdBookmarksCallback; + UpdatedBookmarksCallback m_updatedBookmarksCallback; + DeletedBookmarksCallback m_deletedBookmarksCallback; + UserMarkContainer::Listeners m_bookmarksListeners; + df::DrapeEngineSafePtr m_drapeEngine; AsyncLoadingCallbacks m_asyncLoadingCallbacks; std::atomic m_needTeardown; diff --git a/map/framework.cpp b/map/framework.cpp index 84075c7549..9825b6d736 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -359,7 +359,19 @@ Framework::Framework(FrameworkParams const & params) : m_startForegroundTime(0.0) , m_storage(platform::migrate::NeedMigrate() ? COUNTRIES_OBSOLETE_FILE : COUNTRIES_FILE) , m_enabledDiffs(params.m_enableDiffs) - , m_bmManager([this]() -> StringsBundle const & { return m_stringsBundle; }) + , m_bmManager([this]() -> StringsBundle const & { return m_stringsBundle; }, + [](std::vector> const & marks) + { + // TODO: Add processing of the created marks. + }, + [](std::vector> const & marks) + { + // TODO: Add processing of the updated marks. + }, + [](std::vector const & marks) + { + // TODO: Add processing of the deleted marks. + }) , m_isRenderingEnabled(true) , m_routingManager(RoutingManager::Callbacks([this]() -> Index & { return m_model.GetIndex(); }, [this]() -> storage::CountryInfoGetter & { return GetCountryInfoGetter(); }, diff --git a/map/map_tests/bookmarks_test.cpp b/map/map_tests/bookmarks_test.cpp index b9a55784fd..3a214e1bbf 100644 --- a/map/map_tests/bookmarks_test.cpp +++ b/map/map_tests/bookmarks_test.cpp @@ -157,7 +157,7 @@ char const * kmlString = UNIT_TEST(Bookmarks_ImportKML) { - BookmarkCategory cat("Default"); + BookmarkCategory cat("Default", UserMarkContainer::Listeners()); TEST(cat.LoadFromKML(make_unique(kmlString, strlen(kmlString))), ()); CheckBookmarks(cat); @@ -171,7 +171,7 @@ UNIT_TEST(Bookmarks_ExportKML) { char const * BOOKMARKS_FILE_NAME = "UnitTestBookmarks.kml"; - BookmarkCategory cat("Default"); + BookmarkCategory cat("Default", UserMarkContainer::Listeners()); TEST(cat.LoadFromKML(make_unique(kmlString, strlen(kmlString))), ()); CheckBookmarks(cat); @@ -191,7 +191,7 @@ UNIT_TEST(Bookmarks_ExportKML) CheckBookmarks(cat); TEST_EQUAL(cat.IsVisible(), true, ()); - auto cat2 = BookmarkCategory::CreateFromKMLFile(BOOKMARKS_FILE_NAME); + auto cat2 = BookmarkCategory::CreateFromKMLFile(BOOKMARKS_FILE_NAME, UserMarkContainer::Listeners()); CheckBookmarks(*cat2); TEST(cat2->SaveToKMLFile(), ()); @@ -201,7 +201,7 @@ UNIT_TEST(Bookmarks_ExportKML) // MapName is the tag in test kml data. string const catFileName = GetPlatform().SettingsDir() + "MapName.kml"; - cat2 = BookmarkCategory::CreateFromKMLFile(catFileName); + cat2 = BookmarkCategory::CreateFromKMLFile(catFileName, UserMarkContainer::Listeners()); CheckBookmarks(*cat2); TEST(my::DeleteFileX(catFileName), ()); } @@ -536,7 +536,7 @@ char const * kmlString2 = UNIT_TEST(Bookmarks_InnerFolder) { - BookmarkCategory cat("Default"); + BookmarkCategory cat("Default", UserMarkContainer::Listeners()); TEST(cat.LoadFromKML(make_unique(kmlString2, strlen(kmlString2))), ()); TEST_EQUAL(cat.GetUserMarkCount(), 1, ()); @@ -544,7 +544,7 @@ UNIT_TEST(Bookmarks_InnerFolder) UNIT_TEST(BookmarkCategory_EmptyName) { - unique_ptr pCat(new BookmarkCategory("")); + unique_ptr pCat(new BookmarkCategory("", UserMarkContainer::Listeners())); static_cast(pCat->CreateUserMark(m2::PointD(0, 0)))->SetData(BookmarkData("", "placemark-red")); TEST(pCat->SaveToKMLFile(), ()); @@ -593,13 +593,14 @@ char const * kmlString3 = UNIT_TEST(Bookmarks_SpecialXMLNames) { - BookmarkCategory cat1(""); + BookmarkCategory cat1("", UserMarkContainer::Listeners()); TEST(cat1.LoadFromKML(make_unique(kmlString3, strlen(kmlString3))), ()); TEST_EQUAL(cat1.GetUserMarkCount(), 1, ()); TEST(cat1.SaveToKMLFile(), ()); - unique_ptr const cat2(BookmarkCategory::CreateFromKMLFile(cat1.GetFileName())); + unique_ptr const cat2(BookmarkCategory::CreateFromKMLFile(cat1.GetFileName(), + UserMarkContainer::Listeners())); TEST(cat2.get(), ()); TEST_EQUAL(cat2->GetUserMarkCount(), 1, ()); @@ -617,7 +618,7 @@ UNIT_TEST(Bookmarks_SpecialXMLNames) UNIT_TEST(TrackParsingTest_1) { string const kmlFile = GetPlatform().TestsDataPathForFile("kml-with-track-kml.test"); - auto cat = BookmarkCategory::CreateFromKMLFile(kmlFile); + auto cat = BookmarkCategory::CreateFromKMLFile(kmlFile, UserMarkContainer::Listeners()); TEST(cat, ("Category can't be created")); TEST_EQUAL(cat->GetTracksCount(), 4, ()); @@ -642,7 +643,7 @@ UNIT_TEST(TrackParsingTest_1) UNIT_TEST(TrackParsingTest_2) { string const kmlFile = GetPlatform().TestsDataPathForFile("kml-with-track-from-google-earth.test"); - auto cat = BookmarkCategory::CreateFromKMLFile(kmlFile); + auto cat = BookmarkCategory::CreateFromKMLFile(kmlFile, UserMarkContainer::Listeners()); TEST(cat, ("Category can't be created")); TEST_EQUAL(cat->GetTracksCount(), 1, ()); diff --git a/map/map_tests/kmz_unarchive_test.cpp b/map/map_tests/kmz_unarchive_test.cpp index 85c4cbcdb3..3c0d62f4cc 100644 --- a/map/map_tests/kmz_unarchive_test.cpp +++ b/map/map_tests/kmz_unarchive_test.cpp @@ -35,7 +35,7 @@ UNIT_TEST(KMZ_UnzipTest) MY_SCOPE_GUARD(fileGuard, bind(&FileWriter::DeleteFileX, kmlFile)); ZipFileReader::UnzipFile(kmzFile, "doc.kml", kmlFile); - BookmarkCategory cat("Default"); + BookmarkCategory cat("Default", UserMarkContainer::Listeners()); TEST(cat.LoadFromKML(make_unique(kmlFile)), ()); TEST_EQUAL(files.size(), 6, ("KMZ file wrong number of files")); diff --git a/map/user_mark_container.cpp b/map/user_mark_container.cpp index c53b617fe4..0e7174450c 100644 --- a/map/user_mark_container.cpp +++ b/map/user_mark_container.cpp @@ -53,9 +53,11 @@ df::MarkGroupID GenerateMarkGroupId(UserMarkContainer const * cont) } } // namespace -UserMarkContainer::UserMarkContainer(double layerDepth, UserMark::Type type) +UserMarkContainer::UserMarkContainer(double layerDepth, UserMark::Type type, + Listeners const & listeners) : m_layerDepth(layerDepth) , m_type(type) + , m_listeners(listeners) { m_flags.set(); } @@ -71,6 +73,21 @@ void UserMarkContainer::SetDrapeEngine(ref_ptr engine) m_drapeEngine.Set(engine); } +void UserMarkContainer::SetListeners(Listeners const & listeners) +{ + m_listeners = listeners; +} + +UserMark const * UserMarkContainer::GetUserMarkById(df::MarkID id) const +{ + for (auto const & mark : m_userMarks) + { + if (mark->GetId() == id) + return mark.get(); + } + return nullptr; +} + UserMark const * UserMarkContainer::FindMarkInRect(m2::AnyRectD const & rect, double & d) const { UserMark * mark = nullptr; @@ -86,11 +103,41 @@ UserMark const * UserMarkContainer::FindMarkInRect(m2::AnyRectD const & rect, do return mark; } +void UserMarkContainer::NotifyListeners() +{ + if (!IsDirty()) + return; + + if (m_listeners.m_createListener != nullptr && !m_createdMarks.empty()) + { + df::IDCollection marks(m_createdMarks.begin(), m_createdMarks.end()); + m_listeners.m_createListener(this, marks); + } + if (m_listeners.m_updateListener != nullptr) + { + df::IDCollection marks; + for (auto const & mark : m_userMarks) + { + if (mark->IsDirty() && m_createdMarks.find(mark->GetId()) == m_createdMarks.end()) + marks.push_back(mark->GetId()); + } + if (!marks.empty()) + m_listeners.m_updateListener(this, marks); + } + if (m_listeners.m_deleteListener != nullptr && !m_removedMarks.empty()) + { + df::IDCollection marks(m_removedMarks.begin(), m_removedMarks.end()); + m_listeners.m_deleteListener(this, marks); + } +} + void UserMarkContainer::NotifyChanges() { if (!IsDirty()) return; + NotifyListeners(); + df::DrapeEngineLockGuard lock(m_drapeEngine); if (!lock) return; diff --git a/map/user_mark_container.hpp b/map/user_mark_container.hpp index 1f4c68a114..04ca6c49e6 100644 --- a/map/user_mark_container.hpp +++ b/map/user_mark_container.hpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -38,11 +39,31 @@ class UserMarkContainer : public df::UserMarksProvider { public: using TUserMarksList = std::deque>; + using NotifyChangesFn = std::function; - UserMarkContainer(double layerDepth, UserMark::Type type); + struct Listeners + { + Listeners() = default; + Listeners(NotifyChangesFn const & createListener, + NotifyChangesFn const & updateListener, + NotifyChangesFn const & deleteListener) + : m_createListener(createListener) + , m_updateListener(updateListener) + , m_deleteListener(deleteListener) + {} + + NotifyChangesFn m_createListener; + NotifyChangesFn m_updateListener; + NotifyChangesFn m_deleteListener; + }; + + UserMarkContainer(double layerDepth, UserMark::Type type, + Listeners const & listeners = Listeners()); ~UserMarkContainer() override; void SetDrapeEngine(ref_ptr engine); + void SetListeners(Listeners const & listeners); + UserMark const * GetUserMarkById(df::MarkID id) const; // If not found mark on rect result is nullptr. // If mark is found in "d" return distance from rect center. @@ -86,6 +107,8 @@ protected: virtual UserMark * AllocateUserMark(m2::PointD const & ptOrg) = 0; private: + void NotifyListeners(); + df::DrapeEngineSafePtr m_drapeEngine; std::bitset<4> m_flags; double m_layerDepth; @@ -95,6 +118,8 @@ private: std::set m_removedMarks; bool m_isDirty = false; + Listeners m_listeners; + DISALLOW_COPY_AND_MOVE(UserMarkContainer); };