From 0d0c91cf7db7ae2fb635fff3da2a8a8de7402e2b Mon Sep 17 00:00:00 2001 From: Daria Volvenkova Date: Thu, 1 Feb 2018 18:17:57 +0300 Subject: [PATCH] Store user marks and tracks inside the BookmarkManager. --- android/jni/com/mapswithme/maps/Framework.cpp | 8 +- android/jni/com/mapswithme/maps/Framework.hpp | 4 +- drape_frontend/drape_engine.cpp | 20 +- drape_frontend/user_marks_global.hpp | 1 + drape_frontend/user_marks_provider.cpp | 15 - drape_frontend/user_marks_provider.hpp | 26 +- iphone/Maps/Bookmarks/AddSetVC.h | 4 +- iphone/Maps/Bookmarks/AddSetVC.mm | 2 +- iphone/Maps/Bookmarks/BookmarksRootVC.mm | 18 +- iphone/Maps/Bookmarks/BookmarksVC.h | 8 +- iphone/Maps/Bookmarks/BookmarksVC.mm | 54 +- iphone/Maps/Bookmarks/SelectSetVC.h | 6 +- iphone/Maps/Bookmarks/SelectSetVC.mm | 24 +- iphone/Maps/Classes/MapViewController.mm | 2 +- .../EditBookmark/MWMEditBookmarkController.mm | 35 +- iphone/Maps/UI/PlacePage/MWMPlacePageData.h | 4 +- iphone/Maps/UI/PlacePage/MWMPlacePageData.mm | 31 +- .../Maps/UI/PlacePage/MWMPlacePageManager.mm | 17 +- map/bookmark.cpp | 377 ++------ map/bookmark.hpp | 81 +- map/bookmark_manager.cpp | 851 ++++++++++++------ map/bookmark_manager.hpp | 182 ++-- map/framework.cpp | 86 +- map/framework.hpp | 21 +- map/local_ads_manager.cpp | 19 +- map/mwm_url.cpp | 17 +- map/place_page_info.cpp | 4 +- map/place_page_info.hpp | 11 +- map/routing_manager.cpp | 22 +- map/routing_mark.cpp | 75 +- map/routing_mark.hpp | 2 - map/search_mark.cpp | 5 +- map/track.cpp | 12 + map/track.hpp | 5 + map/transit/transit_display.cpp | 8 +- map/user_mark.cpp | 8 +- map/user_mark.hpp | 11 +- map/user_mark_container.cpp | 240 +---- map/user_mark_container.hpp | 86 +- 39 files changed, 1140 insertions(+), 1262 deletions(-) diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp index 34f5d82511..f60b3c0c49 100644 --- a/android/jni/com/mapswithme/maps/Framework.cpp +++ b/android/jni/com/mapswithme/maps/Framework.cpp @@ -373,14 +373,14 @@ void Framework::RemoveLocalMaps() m_work.DeregisterAllMaps(); } -void Framework::ReplaceBookmark(BookmarkAndCategory const & ind, BookmarkData & bm) +void Framework::ReplaceBookmark(df::MarkID markId, BookmarkData & bm) { - m_work.ReplaceBookmark(ind.m_categoryIndex, ind.m_bookmarkIndex, bm); + m_work.ReplaceBookmark(markId, ind.m_bookmarkIndex, bm); } -size_t Framework::ChangeBookmarkCategory(BookmarkAndCategory const & ind, size_t newCat) +size_t Framework::ChangeBookmarkCategory(df::MarkID markId, df::MarkGroupID newCat) { - return m_work.MoveBookmark(ind.m_bookmarkIndex, ind.m_categoryIndex, newCat); + return m_work.MoveBookmark(markId, newCat); } bool Framework::ShowMapForURL(string const & url) diff --git a/android/jni/com/mapswithme/maps/Framework.hpp b/android/jni/com/mapswithme/maps/Framework.hpp index f38a3ed411..3c8f0bf657 100644 --- a/android/jni/com/mapswithme/maps/Framework.hpp +++ b/android/jni/com/mapswithme/maps/Framework.hpp @@ -138,8 +138,8 @@ namespace android void Scale(::Framework::EScaleMode mode); void Scale(m2::PointD const & centerPt, int targetZoom, bool animate); - void ReplaceBookmark(BookmarkAndCategory const & ind, BookmarkData & bm); - size_t ChangeBookmarkCategory(BookmarkAndCategory const & ind, size_t newCat); + void ReplaceBookmark(df::MarkID markId, BookmarkData & bm); + size_t ChangeBookmarkCategory(df::MarkID markId, df::MarkGroupID newCat); ::Framework * NativeFramework(); diff --git a/drape_frontend/drape_engine.cpp b/drape_frontend/drape_engine.cpp index 81ca0a183e..ea20ae6ecd 100644 --- a/drape_frontend/drape_engine.cpp +++ b/drape_frontend/drape_engine.cpp @@ -227,13 +227,14 @@ void DrapeEngine::UpdateUserMarksGroup(MarkGroupID groupId, UserMarksProvider * auto removedIdCollection = make_unique_dp(); auto createdIdCollection = make_unique_dp(); - auto marksRenderCollection = make_unique_dp(); - marksRenderCollection->reserve(provider->GetUserPointCount()); + provider->AcceptChanges(groupId, *groupIdCollection, *createdIdCollection, *removedIdCollection); - for (size_t pointIndex = 0, sz = provider->GetUserPointCount(); pointIndex < sz; ++pointIndex) + auto marksRenderCollection = make_unique_dp(); + marksRenderCollection->reserve(groupIdCollection->m_marksID.size()); + + for (auto markId : groupIdCollection->m_marksID) { - UserPointMark const * mark = provider->GetUserPointMark(pointIndex); - groupIdCollection->m_marksID.push_back(mark->GetId()); + UserPointMark const * mark = provider->GetUserPointMark(markId); if (mark->IsDirty()) { auto renderInfo = make_unique_dp(); @@ -263,11 +264,10 @@ void DrapeEngine::UpdateUserMarksGroup(MarkGroupID groupId, UserMarksProvider * } auto linesRenderCollection = make_unique_dp(); - linesRenderCollection->reserve(provider->GetUserLineCount()); - for (size_t lineIndex = 0, sz = provider->GetUserLineCount(); lineIndex < sz; ++lineIndex) + linesRenderCollection->reserve(groupIdCollection->m_linesID.size()); + for (auto lineId : groupIdCollection->m_linesID) { - UserLineMark const * mark = provider->GetUserLineMark(lineIndex); - groupIdCollection->m_linesID.push_back(mark->GetId()); + UserLineMark const * mark = provider->GetUserLineMark(lineId); if (mark->IsDirty()) { auto renderInfo = make_unique_dp(); @@ -287,8 +287,6 @@ void DrapeEngine::UpdateUserMarksGroup(MarkGroupID groupId, UserMarksProvider * } } - provider->AcceptChanges(*createdIdCollection, *removedIdCollection); - if (!createdIdCollection->IsEmpty() || !removedIdCollection->IsEmpty() || !marksRenderCollection->empty() || !linesRenderCollection->empty()) { diff --git a/drape_frontend/user_marks_global.hpp b/drape_frontend/user_marks_global.hpp index d838a01127..6a9c48c9c7 100644 --- a/drape_frontend/user_marks_global.hpp +++ b/drape_frontend/user_marks_global.hpp @@ -5,5 +5,6 @@ namespace df { using MarkID = uint32_t; +using MarkGroupID = size_t; using IDCollection = std::vector; } // namespace df diff --git a/drape_frontend/user_marks_provider.cpp b/drape_frontend/user_marks_provider.cpp index 81d9cd48bb..7301accdc8 100644 --- a/drape_frontend/user_marks_provider.cpp +++ b/drape_frontend/user_marks_provider.cpp @@ -22,19 +22,4 @@ UserLineMark::UserLineMark() { } -UserMarksProvider::UserMarksProvider() - : m_pendingOnDelete(false) -{ -} - -bool UserMarksProvider::IsPendingOnDelete() -{ - return m_pendingOnDelete; -} - -void UserMarksProvider::DeleteLater() -{ - ASSERT(m_pendingOnDelete == false, ()); - m_pendingOnDelete = true; -} } // namespace df diff --git a/drape_frontend/user_marks_provider.hpp b/drape_frontend/user_marks_provider.hpp index 36848d41eb..d0990f7f24 100644 --- a/drape_frontend/user_marks_provider.hpp +++ b/drape_frontend/user_marks_provider.hpp @@ -15,8 +15,6 @@ namespace df { -using MarkGroupID = size_t; - struct MarkIDCollection { IDCollection m_marksID; @@ -101,27 +99,13 @@ private: class UserMarksProvider { public: - UserMarksProvider(); virtual ~UserMarksProvider() {} - - virtual bool IsDirty() const = 0; - virtual void AcceptChanges(MarkIDCollection & createdMarks, MarkIDCollection & removedMarks) = 0; - - virtual bool IsDrawable() const = 0; - - virtual size_t GetUserPointCount() const = 0; + virtual void AcceptChanges(MarkGroupID groupID, + MarkIDCollection & updatedMarks, MarkIDCollection & createdMarks, MarkIDCollection & removedMarks) = 0; /// never store UserPointMark reference - virtual UserPointMark const * GetUserPointMark(size_t index) const = 0; - - virtual size_t GetUserLineCount() const = 0; - /// never store UserLineMark reference - virtual UserLineMark const * GetUserLineMark(size_t index) const = 0; - - bool IsPendingOnDelete(); - void DeleteLater(); - -private: - bool m_pendingOnDelete; + virtual UserPointMark const * GetUserPointMark(MarkID markID) const = 0; + /// never store UserLineMark reference + virtual UserLineMark const * GetUserLineMark(MarkID markID) const = 0; }; } // namespace df diff --git a/iphone/Maps/Bookmarks/AddSetVC.h b/iphone/Maps/Bookmarks/AddSetVC.h index 14081b67df..10ac731933 100644 --- a/iphone/Maps/Bookmarks/AddSetVC.h +++ b/iphone/Maps/Bookmarks/AddSetVC.h @@ -1,9 +1,11 @@ #import "MWMTableViewController.h" +#include "drape_frontend/user_marks_global.hpp" + @class AddSetVC; @protocol AddSetVCDelegate -- (void)addSetVC:(AddSetVC *)vc didAddSetWithCategoryId:(int)categoryId; +- (void)addSetVC:(AddSetVC *)vc didAddSetWithCategoryId:(df::MarkGroupID)categoryId; @end diff --git a/iphone/Maps/Bookmarks/AddSetVC.mm b/iphone/Maps/Bookmarks/AddSetVC.mm index 89ae9420cf..1558c935d0 100644 --- a/iphone/Maps/Bookmarks/AddSetVC.mm +++ b/iphone/Maps/Bookmarks/AddSetVC.mm @@ -37,7 +37,7 @@ if (text.length == 0) return; [self.delegate addSetVC:self - didAddSetWithCategoryId:static_cast(GetFramework().AddCategory(text.UTF8String))]; + didAddSetWithCategoryId:(GetFramework().AddCategory(text.UTF8String))]; [self.navigationController popViewControllerAnimated:YES]; } diff --git a/iphone/Maps/Bookmarks/BookmarksRootVC.mm b/iphone/Maps/Bookmarks/BookmarksRootVC.mm index c1ecc5fdfa..1dff504d08 100644 --- a/iphone/Maps/Bookmarks/BookmarksRootVC.mm +++ b/iphone/Maps/Bookmarks/BookmarksRootVC.mm @@ -52,7 +52,7 @@ extern NSString * const kBookmarkCategoryDeletedNotification = } else { - bool const showDetailedHint = GetFramework().GetBookmarkManager().GetBmCategoriesIds().empty(); + bool const showDetailedHint = GetFramework().GetBookmarkManager().GetBmGroupsIdList().empty(); label.text = showDetailedHint ? L(@"bookmarks_usage_hint") : L(@"bookmarks_usage_hint_import_only"); } @@ -118,7 +118,7 @@ extern NSString * const kBookmarkCategoryDeletedNotification = - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - auto sz = GetFramework().GetBookmarkManager().GetBmCategoriesIds().size(); + auto sz = GetFramework().GetBookmarkManager().GetBmGroupsIdList().size(); return sz; } @@ -126,7 +126,7 @@ extern NSString * const kBookmarkCategoryDeletedNotification = { NSInteger row = ((UITapGestureRecognizer *)sender).view.tag; auto & bmManager = GetFramework().GetBookmarkManager(); - auto categoryId = bmManager.GetBmCategoriesIds()[row]; + auto categoryId = bmManager.GetBmGroupsIdList()[row]; UITableViewCell * cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0]]; if (cell && bmManager.HasBmCategory(categoryId)) { @@ -159,7 +159,7 @@ extern NSString * const kBookmarkCategoryDeletedNotification = cell.imageView.tag = indexPath.row; auto & bmManager = GetFramework().GetBookmarkManager(); - size_t const categoryIndex = bmManager.GetBmCategoriesIds()[indexPath.row]; + size_t const categoryIndex = bmManager.GetBmGroupsIdList()[indexPath.row]; if (bmManager.HasBmCategory(categoryIndex)) { NSString * title = @(bmManager.GetCategoryName(categoryIndex).c_str()); @@ -168,7 +168,7 @@ extern NSString * const kBookmarkCategoryDeletedNotification = cell.imageView.image = [UIImage imageNamed:(isVisible ? @"ic_show" : @"ic_hide")]; cell.imageView.mwm_coloring = isVisible ? MWMImageColoringBlue : MWMImageColoringBlack; cell.detailTextLabel.text = [NSString stringWithFormat:@"%ld", - bmManager.GetUserMarkCount(categoryIndex) + bmManager.GetTracksCount(categoryIndex)]; + bmManager.GetUserMarkIds(categoryIndex).size() + bmManager.GetTrackIds(categoryIndex).size()]; } cell.backgroundColor = [UIColor white]; cell.textLabel.textColor = [UIColor blackPrimaryText]; @@ -219,7 +219,7 @@ extern NSString * const kBookmarkCategoryDeletedNotification = cell.textLabel.text = txt; // Rename category auto & bmManager = GetFramework().GetBookmarkManager(); - size_t const categoryId = bmManager.GetBmCategoriesIds()[[self.tableView indexPathForCell:cell].row]; + size_t const categoryId = bmManager.GetBmGroupsIdList()[[self.tableView indexPathForCell:cell].row]; if (bmManager.HasBmCategory(categoryId)) { bmManager.SetCategoryName(categoryId, txt.UTF8String); @@ -268,7 +268,7 @@ extern NSString * const kBookmarkCategoryDeletedNotification = } else { - auto categoryId = GetFramework().GetBookmarkManager().GetBmCategoriesIds()[indexPath.row]; + auto categoryId = GetFramework().GetBookmarkManager().GetBmGroupsIdList()[indexPath.row]; BookmarksVC * bvc = [[BookmarksVC alloc] initWithCategory:categoryId]; [self.navigationController pushViewController:bvc animated:YES]; } @@ -290,7 +290,7 @@ extern NSString * const kBookmarkCategoryDeletedNotification = f.DeleteBmCategory(indexPath.row); [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; // Disable edit mode if no categories are left - if (f.GetBookmarkManager().GetBmCategoriesIds().empty()) + if (f.GetBookmarkManager().GetBmGroupsIdList().empty()) { self.navigationItem.rightBarButtonItem = nil; [self setEditing:NO animated:YES]; @@ -302,7 +302,7 @@ extern NSString * const kBookmarkCategoryDeletedNotification = { [super viewWillAppear:animated]; // Display Edit button only if table is not empty - if (!GetFramework().GetBookmarkManager().GetBmCategoriesIds().empty()) + if (!GetFramework().GetBookmarkManager().GetBmGroupsIdList().empty()) self.navigationItem.rightBarButtonItem = self.editButtonItem; else self.navigationItem.rightBarButtonItem = nil; diff --git a/iphone/Maps/Bookmarks/BookmarksVC.h b/iphone/Maps/Bookmarks/BookmarksVC.h index eab68eff3b..1f817ff843 100644 --- a/iphone/Maps/Bookmarks/BookmarksVC.h +++ b/iphone/Maps/Bookmarks/BookmarksVC.h @@ -1,10 +1,14 @@ #import "MWMTableViewController.h" +#include "drape_frontend/user_marks_global.hpp" + @interface BookmarksVC : MWMTableViewController { - NSUInteger m_categoryId; + df::MarkGroupID m_categoryId; + NSMutableArray * m_bookmarkIds; + NSMutableArray * m_trackIds; } -- (instancetype)initWithCategory:(NSUInteger)index; +- (instancetype)initWithCategory:(df::MarkGroupID)index; @end diff --git a/iphone/Maps/Bookmarks/BookmarksVC.mm b/iphone/Maps/Bookmarks/BookmarksVC.mm index 05d83cb73e..6bb0569244 100644 --- a/iphone/Maps/Bookmarks/BookmarksVC.mm +++ b/iphone/Maps/Bookmarks/BookmarksVC.mm @@ -33,13 +33,23 @@ extern NSString * const kBookmarkDeletedNotification = @"BookmarkDeletedNotifica @implementation BookmarksVC -- (instancetype)initWithCategory:(NSUInteger)index +- (instancetype)initWithCategory:(df::MarkGroupID)index { self = [super initWithStyle:UITableViewStyleGrouped]; if (self) { m_categoryId = index; - self.title = @(GetFramework().GetBookmarkManager().GetCategoryName(index).c_str()); + auto const & bmManager = GetFramework().GetBookmarkManager(); + self.title = @(bmManager.GetCategoryName(m_categoryId).c_str()); + auto const & bookmarkIds = bmManager.GetUserMarkIds(m_categoryId); + auto const & trackIds = bmManager.GetTrackIds(m_categoryId); + // TODO(darina): should we release these arrays manually? + m_bookmarkIds = [NSMutableArray arrayWithCapacity:bookmarkIds.size()]; + m_trackIds = [NSMutableArray arrayWithCapacity:trackIds.size()]; + for (auto bookmarkId : bookmarkIds) + [m_bookmarkIds addObject:[NSNumber numberWithInt:bookmarkId]]; + for (auto trackId : trackIds) + [m_trackIds addObject:[NSNumber numberWithInt:trackId]]; [self calculateSections]; } return self; @@ -61,9 +71,9 @@ extern NSString * const kBookmarkDeletedNotification = @"BookmarkDeletedNotifica if (section == 0) return 2; else if (section == m_trackSection) - return GetFramework().GetBookmarkManager().GetTracksCount(m_categoryId); + return GetFramework().GetBookmarkManager().GetTrackIds(m_categoryId).size(); else if (section == m_bookmarkSection) - return GetFramework().GetBookmarkManager().GetUserMarkCount(m_categoryId); + return GetFramework().GetBookmarkManager().GetUserMarkIds(m_categoryId).size(); else if (section == m_shareSection) return 1; else @@ -129,7 +139,8 @@ extern NSString * const kBookmarkDeletedNotification = @"BookmarkDeletedNotifica cell = [tableView dequeueReusableCellWithIdentifier:@"TrackCell"]; if (!cell) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"TrackCell"]; - Track const * tr = bmManager.GetTrack(m_categoryId, indexPath.row); + df::MarkID trackId = [[m_trackIds objectAtIndex:indexPath.row] intValue]; + Track const * tr = bmManager.GetTrack(trackId); cell.textLabel.text = @(tr->GetName().c_str()); string dist; if (measurement_utils::FormatDistance(tr->GetLengthMeters(), dist)) @@ -147,7 +158,8 @@ extern NSString * const kBookmarkDeletedNotification = @"BookmarkDeletedNotifica UITableViewCell * bmCell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"BookmarksVCBookmarkItemCell"]; if (!bmCell) bmCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"BookmarksVCBookmarkItemCell"]; - Bookmark const * bm = bmManager.GetBookmark(m_categoryId, indexPath.row); + df::MarkID bmId = [[m_bookmarkIds objectAtIndex:indexPath.row] intValue]; + Bookmark const * bm = bmManager.GetBookmark(bmId); if (bm) { bmCell.textLabel.text = @(bm->GetName().c_str()); @@ -210,7 +222,8 @@ extern NSString * const kBookmarkDeletedNotification = @"BookmarkDeletedNotifica { if (categoryExists) { - Track const * tr = bmManager.GetTrack(m_categoryId, indexPath.row); + df::MarkID trackId = [[m_trackIds objectAtIndex:indexPath.row] intValue]; + Track const * tr = bmManager.GetTrack(trackId); ASSERT(tr, ("NULL track")); if (tr) { @@ -223,14 +236,15 @@ extern NSString * const kBookmarkDeletedNotification = @"BookmarkDeletedNotifica { if (categoryExists) { - Bookmark const * bm = bmManager.GetBookmark(m_categoryId, indexPath.row); + df::MarkID bmId = [[m_bookmarkIds objectAtIndex:indexPath.row] intValue]; + Bookmark const * bm = bmManager.GetBookmark(bmId); ASSERT(bm, ("NULL bookmark")); if (bm) { [Statistics logEvent:kStatEventName(kStatBookmarks, kStatShowOnMap)]; // Same as "Close". [MWMSearchManager manager].state = MWMSearchManagerStateHidden; - f.ShowBookmark({static_cast(indexPath.row), m_categoryId}); + f.ShowBookmark(bm); [self.navigationController popToRootViewControllerAnimated:YES]; } } @@ -283,15 +297,18 @@ extern NSString * const kBookmarkDeletedNotification = @"BookmarkDeletedNotifica { if (indexPath.section == m_trackSection) { - bmManager.DeleteTrack(m_categoryId, indexPath.row); + df::MarkID trackId = [[m_trackIds objectAtIndex:indexPath.row] intValue]; + bmManager.DeleteTrack(trackId); + [m_trackIds removeObjectAtIndex:indexPath.row]; } else { - auto bac = BookmarkAndCategory(static_cast(indexPath.row), m_categoryId); - NSValue * value = [NSValue valueWithBytes:&bac objCType:@encode(BookmarkAndCategory)]; + df::MarkID bmId = [[m_bookmarkIds objectAtIndex:indexPath.row] intValue]; + NSValue * value = [NSValue valueWithBytes:&bmId objCType:@encode(df::MarkID*)]; [NSNotificationCenter.defaultCenter postNotificationName:kBookmarkDeletedNotification object:value]; - bmManager.DeleteUserMark(m_categoryId, indexPath.row); + bmManager.DeleteUserMark(bmId); + [m_bookmarkIds removeObjectAtIndex:indexPath.row]; [NSNotificationCenter.defaultCenter postNotificationName:kBookmarksChangedNotification object:nil userInfo:nil]; @@ -306,7 +323,7 @@ extern NSString * const kBookmarkDeletedNotification = @"BookmarkDeletedNotifica [self.tableView deleteRowsAtIndexPaths:@[ indexPath ] withRowAnimation:UITableViewRowAnimationFade]; else [self.tableView reloadData]; - if (bmManager.GetUserMarkCount(m_categoryId) + bmManager.GetTracksCount(m_categoryId) == 0) + if (bmManager.GetUserMarkIds(m_categoryId).size() + bmManager.GetTrackIds(m_categoryId).size() == 0) { self.navigationItem.rightBarButtonItem = nil; [self setEditing:NO animated:YES]; @@ -329,7 +346,8 @@ extern NSString * const kBookmarkDeletedNotification = @"BookmarkDeletedNotifica NSIndexPath * indexPath = [table indexPathForCell:cell]; if (indexPath.section == self->m_bookmarkSection) { - Bookmark const * bm = bmManager.GetBookmark(m_categoryId, indexPath.row); + df::MarkID bmId = [[m_bookmarkIds objectAtIndex:indexPath.row] intValue]; + Bookmark const * bm = bmManager.GetBookmark(bmId); if (bm) { m2::PointD const center = bm->GetPivot(); @@ -352,7 +370,7 @@ extern NSString * const kBookmarkDeletedNotification = @"BookmarkDeletedNotifica // Display Edit button only if table is not empty auto & bmManager = GetFramework().GetBookmarkManager(); if (bmManager.HasBmCategory(m_categoryId) - && (bmManager.GetUserMarkCount(m_categoryId) + bmManager.GetTracksCount(m_categoryId))) + && (bmManager.GetUserMarkIds(m_categoryId).size() + bmManager.GetTrackIds(m_categoryId).size())) self.navigationItem.rightBarButtonItem = self.editButtonItem; else self.navigationItem.rightBarButtonItem = nil; @@ -444,11 +462,11 @@ extern NSString * const kBookmarkDeletedNotification = @"BookmarkDeletedNotifica { int index = 1; auto & bmManager = GetFramework().GetBookmarkManager(); - if (bmManager.GetTracksCount(m_categoryId)) + if (bmManager.GetTrackIds(m_categoryId).size()) m_trackSection = index++; else m_trackSection = EMPTY_SECTION; - if (bmManager.GetUserMarkCount(m_categoryId)) + if (bmManager.GetUserMarkIds(m_categoryId).size()) m_bookmarkSection = index++; else m_bookmarkSection = EMPTY_SECTION; diff --git a/iphone/Maps/Bookmarks/SelectSetVC.h b/iphone/Maps/Bookmarks/SelectSetVC.h index 697326e8da..ba4f64c4ca 100644 --- a/iphone/Maps/Bookmarks/SelectSetVC.h +++ b/iphone/Maps/Bookmarks/SelectSetVC.h @@ -1,17 +1,17 @@ #import "MWMTableViewController.h" -struct BookmarkAndCategory; +#include "drape_frontend/user_marks_global.hpp" @protocol MWMSelectSetDelegate -- (void)didSelectCategory:(NSString *)category withCategoryIndex:(size_t)categoryIndex; +- (void)didSelectCategory:(NSString *)category withCategoryId:(df::MarkGroupID)categoryId; @end @interface SelectSetVC : MWMTableViewController - (instancetype)initWithCategory:(NSString *)category - categoryIndex:(size_t)categoryIndex + categoryId:(df::MarkGroupID)categoryId delegate:(id)delegate; @end diff --git a/iphone/Maps/Bookmarks/SelectSetVC.mm b/iphone/Maps/Bookmarks/SelectSetVC.mm index 7ccef80d0d..8e22afb883 100644 --- a/iphone/Maps/Bookmarks/SelectSetVC.mm +++ b/iphone/Maps/Bookmarks/SelectSetVC.mm @@ -7,7 +7,7 @@ @interface SelectSetVC () { - size_t m_categoryIndex; + df::MarkGroupID m_categoryId; } @property (copy, nonatomic) NSString * category; @@ -18,14 +18,14 @@ @implementation SelectSetVC - (instancetype)initWithCategory:(NSString *)category - categoryIndex:(size_t)categoryIndex + categoryId:(df::MarkGroupID)categoryId delegate:(id)delegate { self = [super initWithStyle:UITableViewStyleGrouped]; if (self) { _category = category; - m_categoryIndex = categoryIndex; + m_categoryId = categoryId; _delegate = delegate; } return self; @@ -50,7 +50,7 @@ if (section == 0) return 1; - return GetFramework().GetBookmarkManager().GetBmCategoriesIds().size(); + return GetFramework().GetBookmarkManager().GetBmGroupsIdList().size(); } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath @@ -65,11 +65,11 @@ else { auto & bmManager = GetFramework().GetBookmarkManager(); - auto categoryId = bmManager.GetBmCategoriesIds()[indexPath.row]; + auto categoryId = bmManager.GetBmGroupsIdList()[indexPath.row]; if (bmManager.HasBmCategory(categoryId)) cell.textLabel.text = @(bmManager.GetCategoryName(categoryId).c_str()); - if (m_categoryIndex == categoryId) + if (m_categoryId == categoryId) cell.accessoryType = UITableViewCellAccessoryCheckmark; else cell.accessoryType = UITableViewCellAccessoryNone; @@ -77,14 +77,14 @@ return cell; } -- (void)addSetVC:(AddSetVC *)vc didAddSetWithCategoryId:(int)categoryId +- (void)addSetVC:(AddSetVC *)vc didAddSetWithCategoryId:(df::MarkGroupID)categoryId { [self moveBookmarkToSetWithCategoryId:categoryId]; [self.tableView reloadData]; - [self.delegate didSelectCategory:self.category withCategoryIndex:categoryId]; + [self.delegate didSelectCategory:self.category withCategoryId:categoryId]; } -- (void)moveBookmarkToSetWithCategoryId:(int)categoryId +- (void)moveBookmarkToSetWithCategoryId:(df::MarkGroupID)categoryId { self.category = @(GetFramework().GetBookmarkManager().GetCategoryName(categoryId).c_str()); } @@ -100,9 +100,9 @@ } else { - auto categoryId = GetFramework().GetBookmarkManager().GetBmCategoriesIds()[indexPath.row]; - [self moveBookmarkToSetWithCategoryId:static_cast(categoryId)]; - [self.delegate didSelectCategory:self.category withCategoryIndex:categoryId]; + auto categoryId = GetFramework().GetBookmarkManager().GetBmGroupsIdList()[indexPath.row]; + [self moveBookmarkToSetWithCategoryId:categoryId]; + [self.delegate didSelectCategory:self.category withCategoryId:categoryId]; [self backTap]; } } diff --git a/iphone/Maps/Classes/MapViewController.mm b/iphone/Maps/Classes/MapViewController.mm index 171b48a28c..d988cfb975 100644 --- a/iphone/Maps/Classes/MapViewController.mm +++ b/iphone/Maps/Classes/MapViewController.mm @@ -365,7 +365,7 @@ BOOL gIsFirstMyPositionMode = YES; - (void)openMigration { [self performSegueWithIdentifier:kMigrationSegue sender:self]; } - (void)openBookmarks { - auto const & ids = GetFramework().GetBookmarkManager().GetBmCategoriesIds(); + auto const & ids = GetFramework().GetBookmarkManager().GetBmGroupsIdList(); BOOL const oneCategory = (ids.size() == 1); MWMTableViewController * vc = oneCategory ? [[BookmarksVC alloc] initWithCategory:(ids.front())] : [[BookmarksRootVC alloc] init]; diff --git a/iphone/Maps/UI/EditBookmark/MWMEditBookmarkController.mm b/iphone/Maps/UI/EditBookmark/MWMEditBookmarkController.mm index bdea8ce42e..10d541fe01 100644 --- a/iphone/Maps/UI/EditBookmark/MWMEditBookmarkController.mm +++ b/iphone/Maps/UI/EditBookmark/MWMEditBookmarkController.mm @@ -29,13 +29,14 @@ enum RowInMetaInfo RowsInMetaInfoCount }; -static int const kInvalidCategoryIndex = -1; +static int const kInvalidCategoryId = 0; } // namespace @interface MWMEditBookmarkController () { - BookmarkAndCategory m_cachedBookmarkAndCategory; + df::MarkID m_cachedBookmarkId; + df::MarkGroupID m_cachedBookmarkCatId; } @property (nonatomic) MWMNoteCell * cachedNote; @@ -43,7 +44,7 @@ static int const kInvalidCategoryIndex = -1; @property (copy, nonatomic) NSString * cachedTitle; @property (copy, nonatomic) NSString * cachedColor; @property (copy, nonatomic) NSString * cachedCategory; -@property(nonatomic) int64_t cachedCategoryIndex; +@property(nonatomic) df::MarkGroupID cachedBmCatId; @end @@ -52,14 +53,15 @@ static int const kInvalidCategoryIndex = -1; - (void)viewDidLoad { [super viewDidLoad]; - self.cachedCategoryIndex = kInvalidCategoryIndex; + self.cachedBmCatId = kInvalidCategoryId; auto data = self.data; NSAssert(data, @"Data can't be nil!"); self.cachedDescription = data.bookmarkDescription; self.cachedTitle = data.title; self.cachedCategory = data.bookmarkCategory; self.cachedColor = data.bookmarkColor; - m_cachedBookmarkAndCategory = data.bookmarkAndCategory; + m_cachedBookmarkId = data.bookmarkId; + m_cachedBookmarkCatId = data.bookmarkCategoryId; [self configNavBar]; [self registerCells]; } @@ -92,19 +94,14 @@ static int const kInvalidCategoryIndex = -1; { [self.view endEditing:YES]; auto & f = GetFramework(); - if (self.cachedCategoryIndex != kInvalidCategoryIndex) + if (self.cachedBmCatId != kInvalidCategoryId) { - auto const index = static_cast( - f.MoveBookmark(m_cachedBookmarkAndCategory.m_bookmarkIndex, - m_cachedBookmarkAndCategory.m_categoryIndex, - self.cachedCategoryIndex)); - m_cachedBookmarkAndCategory.m_bookmarkIndex = index; - m_cachedBookmarkAndCategory.m_categoryIndex = self.cachedCategoryIndex; + f.MoveBookmark(m_cachedBookmarkId, m_cachedBookmarkCatId, self.cachedBmCatId); + m_cachedBookmarkCatId = self.cachedBmCatId; } BookmarkManager & bmManager = f.GetBookmarkManager(); - auto bookmark = bmManager.GetBookmarkForEdit(m_cachedBookmarkAndCategory.m_categoryIndex, - m_cachedBookmarkAndCategory.m_bookmarkIndex); + auto bookmark = bmManager.GetBookmarkForEdit(m_cachedBookmarkId); if (!bookmark) return; @@ -112,8 +109,8 @@ static int const kInvalidCategoryIndex = -1; bookmark->SetDescription(self.cachedDescription.UTF8String); bookmark->SetName(self.cachedTitle.UTF8String); - bmManager.SaveToKMLFile(m_cachedBookmarkAndCategory.m_categoryIndex); - bmManager.NotifyChanges(m_cachedBookmarkAndCategory.m_categoryIndex); + bmManager.SaveToKMLFile(m_cachedBookmarkCatId); + bmManager.NotifyChanges(m_cachedBookmarkCatId); f.UpdatePlacePageInfoForCurrentSelection(); [self backTap]; @@ -237,7 +234,7 @@ static int const kInvalidCategoryIndex = -1; case Category: { SelectSetVC * svc = [[SelectSetVC alloc] initWithCategory:self.cachedCategory - categoryIndex:m_cachedBookmarkAndCategory.m_categoryIndex + categoryId:m_cachedBookmarkCatId delegate:self]; [self.navigationController pushViewController:svc animated:YES]; break; @@ -283,10 +280,10 @@ static int const kInvalidCategoryIndex = -1; #pragma mark - MWMSelectSetDelegate -- (void)didSelectCategory:(NSString *)category withCategoryIndex:(size_t)categoryIndex +- (void)didSelectCategory:(NSString *)category withCategoryId:(df::MarkGroupID)categoryId { self.cachedCategory = category; - self.cachedCategoryIndex = categoryIndex; + self.cachedBmCatId = categoryId; [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:Category inSection:MetaInfo]] withRowAnimation:UITableViewRowAnimationAutomatic]; } diff --git a/iphone/Maps/UI/PlacePage/MWMPlacePageData.h b/iphone/Maps/UI/PlacePage/MWMPlacePageData.h index 16020686db..f37e5bd83a 100644 --- a/iphone/Maps/UI/PlacePage/MWMPlacePageData.h +++ b/iphone/Maps/UI/PlacePage/MWMPlacePageData.h @@ -13,7 +13,6 @@ @class MWMPlacePageData; @class MWMUGCReviewVM; -struct BookmarkAndCategory; struct FeatureID; namespace ugc @@ -216,7 +215,8 @@ using NewSectionsAreReady = void (^)(NSRange const & range, MWMPlacePageData * d - (NSString *)bookmarkColor; - (NSString *)bookmarkDescription; - (NSString *)bookmarkCategory; -- (BookmarkAndCategory)bookmarkAndCategory; +- (df::MarkID)bookmarkId; +- (df::MarkGroupID)bookmarkCategoryId; // Local Ads - (NSString *)localAdsURL; diff --git a/iphone/Maps/UI/PlacePage/MWMPlacePageData.mm b/iphone/Maps/UI/PlacePage/MWMPlacePageData.mm index 851b0cd232..a9c3242381 100644 --- a/iphone/Maps/UI/PlacePage/MWMPlacePageData.mm +++ b/iphone/Maps/UI/PlacePage/MWMPlacePageData.mm @@ -432,26 +432,24 @@ NSString * const kUserDefaultsLatLonAsDMSKey = @"UserDefaultsLatLonAsDMS"; auto & bmManager = f.GetBookmarkManager(); if (isBookmark) { - auto const categoryIndex = f.LastEditedBMCategory(); + auto const categoryId = f.LastEditedBMCategory(); BookmarkData bmData{m_info.FormatNewBookmarkName(), f.LastEditedBMType()}; - auto const bookmarkIndex = bmManager.AddBookmark(categoryIndex, self.mercator, bmData); - - auto const * bookmark = bmManager.GetBookmark(categoryIndex, bookmarkIndex); - f.FillBookmarkInfo(*bookmark, {bookmarkIndex, categoryIndex}, m_info); - bmManager.NotifyChanges(categoryIndex); + auto const * bookmark = bmManager.CreateBookmark(self.mercator, bmData, categoryId); + f.FillBookmarkInfo(*bookmark, m_info); + bmManager.NotifyChanges(categoryId); m_sections.insert(m_sections.begin() + 1, Sections::Bookmark); } else { - auto const bac = m_info.GetBookmarkAndCategory(); - auto const * bookmark = bmManager.GetBookmark(bac.m_categoryIndex, bac.m_bookmarkIndex); + auto const bookmarkId = m_info.GetBookmarkId(); + auto const * bookmark = bmManager.GetBookmark(bookmarkId); if (bookmark) { f.ResetBookmarkInfo(*bookmark, m_info); - - bmManager.DeleteUserMark(bac.m_categoryIndex, bac.m_bookmarkIndex); - bmManager.NotifyChanges(bac.m_categoryIndex); - bmManager.SaveToKMLFile(bac.m_categoryIndex); + auto const categoryId = bookmark->GetGroupId(); + bmManager.DeleteBookmark(bookmarkId); + bmManager.NotifyChanges(categoryId); + bmManager.SaveToKMLFile(categoryId); } m_sections.erase(remove(m_sections.begin(), m_sections.end(), Sections::Bookmark)); @@ -695,9 +693,14 @@ NSString * const kUserDefaultsLatLonAsDMSKey = @"UserDefaultsLatLonAsDMS"; return m_info.IsBookmark() ? @(m_info.GetBookmarkCategoryName().c_str()) : nil; } -- (BookmarkAndCategory)bookmarkAndCategory +- (df::MarkID)bookmarkId { - return m_info.IsBookmark() ? m_info.GetBookmarkAndCategory() : BookmarkAndCategory(); + return m_info.IsBookmark() ? m_info.GetBookmarkId() : 0; +} + +- (df::MarkGroupID)bookmarkCategoryId +{ + return m_info.IsBookmark() ? m_info.GetBookmarkCategoryId() : 0; } #pragma mark - Local Ads diff --git a/iphone/Maps/UI/PlacePage/MWMPlacePageManager.mm b/iphone/Maps/UI/PlacePage/MWMPlacePageManager.mm index 95c63be113..435287edbf 100644 --- a/iphone/Maps/UI/PlacePage/MWMPlacePageManager.mm +++ b/iphone/Maps/UI/PlacePage/MWMPlacePageManager.mm @@ -128,13 +128,10 @@ void logSponsoredEvent(MWMPlacePageData * data, NSString * eventName) return; auto value = static_cast(notification.object); - auto deletedBookmarkAndCategory = BookmarkAndCategory(); - [value getValue:&deletedBookmarkAndCategory]; - NSAssert(deletedBookmarkAndCategory.IsValid(), - @"Place page must have valid bookmark and category."); - auto bookmarkAndCategory = data.bookmarkAndCategory; - if (bookmarkAndCategory.m_bookmarkIndex != deletedBookmarkAndCategory.m_bookmarkIndex || - bookmarkAndCategory.m_categoryIndex != deletedBookmarkAndCategory.m_categoryIndex) + df::MarkID deletedBookmarkId = 0; + [value getValue:&deletedBookmarkId]; + auto bookmarkId = data.bookmarkId; + if (bookmarkId != deletedBookmarkId) return; [self closePlacePage]; @@ -147,9 +144,9 @@ void logSponsoredEvent(MWMPlacePageData * data, NSString * eventName) if (!data.isBookmark) return; - auto deletedIndex = static_cast(notification.object).integerValue; - auto index = data.bookmarkAndCategory.m_categoryIndex; - if (index != deletedIndex) + auto deletedCategoryId = static_cast(notification.object).integerValue; + auto categoryId = data.bookmarkCategoryId; + if (categoryId != deletedCategoryId) return; [self closePlacePage]; diff --git a/map/bookmark.cpp b/map/bookmark.cpp index 4ac8e54d03..400013836f 100644 --- a/map/bookmark.cpp +++ b/map/bookmark.cpp @@ -8,9 +8,9 @@ #include "geometry/mercator.hpp" #include "coding/file_reader.hpp" +#include "coding/hex.hpp" #include "coding/parse_xml.hpp" // LoadFromKML #include "coding/internal/file_data.hpp" -#include "coding/hex.hpp" #include "drape/drape_global.hpp" #include "drape/color.hpp" @@ -28,15 +28,15 @@ #include #include -Bookmark::Bookmark(m2::PointD const & ptOrg, size_t categoryId) +Bookmark::Bookmark(m2::PointD const & ptOrg) : Base(ptOrg, UserMark::BOOKMARK) - , m_categoryId(categoryId) + , m_groupId(0) {} -Bookmark::Bookmark(BookmarkData const & data, m2::PointD const & ptOrg, size_t categoryId) +Bookmark::Bookmark(BookmarkData const & data, m2::PointD const & ptOrg) : Base(ptOrg, UserMark::BOOKMARK) , m_data(data) - , m_categoryId(categoryId) + , m_groupId(0) {} void Bookmark::SetData(BookmarkData const & data) @@ -125,23 +125,28 @@ void Bookmark::SetScale(double scale) m_data.SetScale(scale); } -void BookmarkCategory::AddTrack(std::unique_ptr && track) +df::MarkGroupID Bookmark::GetGroupId() const { - SetDirty(); - m_tracks.push_back(move(track)); + return m_groupId; } -Track const * BookmarkCategory::GetTrack(size_t index) const +void Bookmark::Attach(df::MarkGroupID groupID) { - return (index < m_tracks.size() ? m_tracks[index].get() : 0); + ASSERT(!m_groupId, ()); + m_groupId = groupID; +} + +void Bookmark::Detach() +{ + m_groupId = 0; } BookmarkCategory::BookmarkCategory(std::string const & name, - size_t index, + df::MarkGroupID groupID, Listeners const & listeners) : Base(UserMark::Type::BOOKMARK, listeners) + , m_groupID(groupID) , m_name(name) - , m_index(index) {} BookmarkCategory::~BookmarkCategory() @@ -154,36 +159,32 @@ size_t BookmarkCategory::GetUserLineCount() const return m_tracks.size(); } -df::UserLineMark const * BookmarkCategory::GetUserLineMark(size_t index) const -{ - ASSERT_LESS(index, m_tracks.size(), ()); - return m_tracks[index].get(); -} - void BookmarkCategory::ClearTracks() { SetDirty(); m_tracks.clear(); } -void BookmarkCategory::DeleteTrack(size_t index) +void BookmarkCategory::AcceptChanges(df::MarkIDCollection & groupMarks, + df::MarkIDCollection & createdMarks, + df::MarkIDCollection & removedMarks) { - SetDirty(); - ASSERT_LESS(index, m_tracks.size(), ()); - m_tracks.erase(next(m_tracks.begin(), index)); + Base::AcceptChanges(groupMarks, createdMarks, removedMarks); + groupMarks.m_linesID.reserve(m_tracks.size()); + for(auto const & trackID : m_tracks) + groupMarks.m_linesID.push_back(trackID); } -std::vector> BookmarkCategory::StealTracks() -{ - std::vector> tracks; - std::swap(m_tracks, tracks); - return tracks; -} - -void BookmarkCategory::AppendTracks(std::vector> && tracks) +void BookmarkCategory::AttachTrack(df::MarkID trackId) { SetDirty(); - std::move(tracks.begin(), tracks.end(), std::back_inserter(m_tracks)); + m_tracks.insert(trackId); +} + +void BookmarkCategory::DetachTrack(df::MarkID trackId) +{ + SetDirty(); + m_userMarks.erase(trackId); } namespace @@ -229,7 +230,7 @@ class KMLParser return style::GetSupportedStyle(result, m_name, style::GetDefaultStyle()); } - BookmarkCategory & m_category; + KMLData & m_data; std::vector m_tags; GeometryType m_geometryType; @@ -368,8 +369,8 @@ class KMLParser } public: - KMLParser(BookmarkCategory & cat) - : m_category(cat) + KMLParser(KMLData & data) + : m_data(data) { Reset(); } @@ -417,17 +418,16 @@ public: { if (GEOMETRY_TYPE_POINT == m_geometryType) { - Bookmark * bm = static_cast(m_category.CreateUserMark(m_org)); - bm->SetData(BookmarkData(m_name, m_type, m_description, m_scale, m_timeStamp)); + m_data.m_bookmarks.emplace_back(std::make_unique( + BookmarkData(m_name, m_type, m_description, m_scale, m_timeStamp), m_org)); } else if (GEOMETRY_TYPE_LINE == m_geometryType) { Track::Params params; params.m_colors.push_back({ kDefaultTrackWidth, m_trackColor }); params.m_name = m_name; - - /// @todo Add description, style, timestamp - m_category.AddTrack(make_unique(m_points, params)); + + m_data.m_tracks.emplace_back(std::make_unique(m_points, params)); } } Reset(); @@ -461,9 +461,9 @@ public: if (prevTag == kDocument) { if (currTag == "name") - m_category.SetName(value); + m_data.m_name = value; else if (currTag == "visibility") - m_category.SetIsVisible(value == "0" ? false : true); + m_data.m_visible = value == "0" ? false : true; } else if (prevTag == kPlacemark) { @@ -574,295 +574,24 @@ std::string BookmarkCategory::GetDefaultType() return style::GetDefaultStyle(); } -bool BookmarkCategory::LoadFromKML(ReaderPtr const & reader) +std::unique_ptr LoadKMLFile(std::string const & file) { - ReaderSource > src(reader); - KMLParser parser(*this); - if (!ParseXML(src, parser, true)) - { - LOG(LWARNING, ("XML read error. Probably, incorrect file encoding.")); - return false; - } - return true; -} - -// static -std::unique_ptr BookmarkCategory::CreateFromKMLFile(std::string const & file, - size_t index, - Listeners const & listeners) -{ - auto cat = my::make_unique("", index, listeners); + auto data = std::make_unique(); + data->m_file = file; try { - if (cat->LoadFromKML(my::make_unique(file))) - cat->m_file = file; - else - cat.reset(); + ReaderSource > src(std::make_unique(file)); + KMLParser parser(*data); + if (!ParseXML(src, parser, true)) + { + LOG(LWARNING, ("XML read error. Probably, incorrect file encoding.")); + data.reset(); + } } catch (std::exception const & e) { LOG(LWARNING, ("Error while loading bookmarks from", file, e.what())); - cat.reset(); + data.reset(); } - - return cat; -} - -namespace -{ -char const * kmlHeader = - "\n" - "\n" - "\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" -; - -char const * kmlFooter = - "\n" - "\n"; -} - -namespace -{ - inline void SaveStringWithCDATA(std::ostream & stream, std::string const & s) - { - // According to kml/xml spec, we need to escape special symbols with CDATA - if (s.find_first_of("<&") != std::string::npos) - stream << ""; - else - stream << s; - } -} - -void BookmarkCategory::SaveToKML(std::ostream & s) -{ - s << kmlHeader; - - // Use CDATA if we have special symbols in the name - s << " "; - SaveStringWithCDATA(s, GetName()); - s << "\n"; - - s << " " << (IsVisible() ? "1" : "0") <<"\n"; - - // Bookmarks are stored to KML file in reverse order, so, least - // recently added bookmark will be stored last. The reason is that - // when bookmarks will be loaded from the KML file, most recently - // added bookmark will be loaded last and in accordance with current - // logic will added to the beginning of the bookmarks list. Thus, - // this method preserves LRU bookmarks order after store -> load - // actions. - // - // Loop invariant: on each iteration count means number of already - // stored bookmarks and i means index of the bookmark that should be - // processed during the iteration. That's why i is initially set to - // GetBookmarksCount() - 1, i.e. to the last bookmark in the - // bookmarks list. - for (size_t count = 0, i = GetUserMarkCount() - 1; - count < GetUserPointCount(); ++count, --i) - { - Bookmark const * bm = static_cast(GetUserMark(i)); - s << " \n"; - s << " "; - SaveStringWithCDATA(s, bm->GetName()); - s << "\n"; - - if (!bm->GetDescription().empty()) - { - s << " "; - SaveStringWithCDATA(s, bm->GetDescription()); - s << "\n"; - } - - time_t const timeStamp = bm->GetTimeStamp(); - if (timeStamp != my::INVALID_TIME_STAMP) - { - std::string const strTimeStamp = my::TimestampToString(timeStamp); - ASSERT_EQUAL(strTimeStamp.size(), 20, ("We always generate fixed length UTC-format timestamp")); - s << " " << strTimeStamp << "\n"; - } - - s << " #" << bm->GetType() << "\n" - << " " << PointToString(bm->GetPivot()) << "\n"; - - double const scale = bm->GetScale(); - if (scale != -1.0) - { - /// @todo Factor out to separate function to use for other custom params. - s << " \n" - << " " << bm->GetScale() << "\n" - << " \n"; - } - - s << " \n"; - } - - // Saving tracks - for (size_t i = 0; i < GetTracksCount(); ++i) - { - Track const * track = GetTrack(i); - - s << " \n"; - s << " "; - SaveStringWithCDATA(s, track->GetName()); - s << "\n"; - - ASSERT_GREATER(track->GetLayerCount(), 0, ()); - - s << "\n"; - // stop style saving - - s << " "; - - Track::PolylineD const & poly = track->GetPolyline(); - for (auto pt = poly.Begin(); pt != poly.End(); ++pt) - s << PointToString(*pt) << " "; - - s << " \n" - << " \n"; - } - - s << kmlFooter; -} - -UserMark * BookmarkCategory::AllocateUserMark(m2::PointD const & ptOrg) -{ - return new Bookmark(ptOrg, m_index); -} - -bool BookmarkCategory::SaveToKMLFile() -{ - std::string oldFile; - - // Get valid file name from category name - std::string const name = BookmarkManager::RemoveInvalidSymbols(m_name); - - if (!m_file.empty()) - { - size_t i2 = m_file.find_last_of('.'); - if (i2 == std::string::npos) - i2 = m_file.size(); - size_t i1 = m_file.find_last_of("\\/"); - if (i1 == std::string::npos) - i1 = 0; - else - ++i1; - - // If m_file doesn't match name, assign new m_file for this category and save old file name. - if (m_file.substr(i1, i2 - i1).find(name) != 0) - { - oldFile = BookmarkManager::GenerateUniqueFileName(GetPlatform().SettingsDir(), name); - m_file.swap(oldFile); - } - } - else - { - m_file = BookmarkManager::GenerateUniqueFileName(GetPlatform().SettingsDir(), name); - } - - std::string const fileTmp = m_file + ".tmp"; - - try - { - // First, we save to the temporary file - /// @todo On Windows UTF-8 file names are not supported. - std::ofstream of(fileTmp.c_str(), std::ios_base::out | std::ios_base::trunc); - SaveToKML(of); - of.flush(); - - if (!of.fail()) - { - // Only after successfull save we replace original file - my::DeleteFileX(m_file); - VERIFY(my::RenameFileX(fileTmp, m_file), (fileTmp, m_file)); - // delete old file - if (!oldFile.empty()) - VERIFY(my::DeleteFileX(oldFile), (oldFile, m_file)); - - return true; - } - } - catch (std::exception const & e) - { - LOG(LWARNING, ("Exception while saving bookmarks:", e.what())); - } - - LOG(LWARNING, ("Can't save bookmarks category", m_name, "to file", m_file)); - - // remove possibly left tmp file - my::DeleteFileX(fileTmp); - - // return old file name in case of error - if (!oldFile.empty()) - m_file.swap(oldFile); - - return false; + return data; } diff --git a/map/bookmark.hpp b/map/bookmark.hpp index 7e7d87cb46..1c92973f9c 100644 --- a/map/bookmark.hpp +++ b/map/bookmark.hpp @@ -74,9 +74,9 @@ class Bookmark : public UserMark { using Base = UserMark; public: - Bookmark(m2::PointD const & ptOrg, size_t categoryId); + Bookmark(m2::PointD const & ptOrg); - Bookmark(BookmarkData const & data, m2::PointD const & ptOrg, size_t categoryId); + Bookmark(BookmarkData const & data, m2::PointD const & ptOrg); void SetData(BookmarkData const & data); BookmarkData const & GetData() const; @@ -102,11 +102,13 @@ public: double GetScale() const; void SetScale(double scale); - size_t GetCategoryId() const { return m_categoryId; } + df::MarkGroupID GetGroupId() const override; + void Attach(df::MarkGroupID groupId); + void Detach(); private: BookmarkData m_data; - size_t m_categoryId; + df::MarkGroupID m_groupId; }; class BookmarkCategory : public UserMarkContainer @@ -114,74 +116,47 @@ class BookmarkCategory : public UserMarkContainer using Base = UserMarkContainer; public: - BookmarkCategory(std::string const & name, size_t index, Listeners const & listeners); + BookmarkCategory(std::string const & name, df::MarkGroupID groupID, Listeners const & listeners); ~BookmarkCategory() override; -protected: - friend class BookmarkManager; - friend class KMLParser; - size_t GetUserLineCount() const override; - df::UserLineMark const * GetUserLineMark(size_t index) const override; static std::string GetDefaultType(); - void ClearTracks(); + void AcceptChanges(df::MarkIDCollection & groupMarks, + df::MarkIDCollection & createdMarks, + df::MarkIDCollection & removedMarks) override; - void AddTrack(std::unique_ptr && track); - Track const * GetTrack(size_t index) const; - inline size_t GetTracksCount() const { return m_tracks.size(); } - void DeleteTrack(size_t index); + void AttachTrack(df::MarkID markId); + void DetachTrack(df::MarkID markId); - std::vector> StealTracks(); - void AppendTracks(std::vector> && tracks); + df::MarkGroupID GetID() const { return m_groupID; } + MarkIDSet const & GetTracks() const { return m_tracks; } void SetName(std::string const & name) { m_name = name; } + void SetFileName(std::string const & fileName) { m_file = fileName; } std::string const & GetName() const { return m_name; } std::string const & GetFileName() const { return m_file; } - /// @name Theese fuctions are public for unit tests only. - /// You don't need to call them from client code. - //@{ - bool LoadFromKML(ReaderPtr const & reader); - void SaveToKML(std::ostream & s); - - /// Uses the same file name from which was loaded, or - /// creates unique file name on first save and uses it every time. - bool SaveToKMLFile(); - - /// @return nullptr in the case of error - static std::unique_ptr CreateFromKMLFile(std::string const & file, - size_t index, - Listeners const & listeners); - //@} - -protected: - UserMark * AllocateUserMark(m2::PointD const & ptOrg) override; - private: - std::vector> m_tracks; + void ClearTracks(); + const df::MarkGroupID m_groupID; std::string m_name; - const size_t m_index; // Stores file name from which bookmarks were loaded. std::string m_file; + + MarkIDSet m_tracks; + }; -struct BookmarkAndCategory +struct KMLData { - BookmarkAndCategory() = default; - BookmarkAndCategory(size_t bookmarkIndex, size_t categoryIndex) - : m_bookmarkIndex(bookmarkIndex) - , m_categoryIndex(categoryIndex) - {} - - bool IsValid() const - { - return m_bookmarkIndex != numeric_limits::max() && - m_categoryIndex != numeric_limits::max(); - }; - - size_t m_bookmarkIndex = numeric_limits::max(); - size_t m_categoryIndex = numeric_limits::max(); + std::string m_name; + std::string m_file; + std::vector> m_bookmarks; + std::vector> m_tracks; + bool m_visible = true; }; + +std::unique_ptr LoadKMLFile(std::string const & file); diff --git a/map/bookmark_manager.cpp b/map/bookmark_manager.cpp index 65a0bad1d7..ad850b6c4e 100644 --- a/map/bookmark_manager.cpp +++ b/map/bookmark_manager.cpp @@ -15,6 +15,7 @@ #include "coding/file_name_utils.hpp" #include "coding/file_writer.hpp" +#include "coding/hex.hpp" #include "coding/internal/file_data.hpp" #include "coding/zip_reader.hpp" @@ -26,6 +27,7 @@ #include "std/target_os.hpp" #include +#include using namespace std::placeholders; @@ -35,14 +37,6 @@ char const * BOOKMARK_CATEGORY = "LastBookmarkCategory"; char const * BOOKMARK_TYPE = "LastBookmarkType"; char const * KMZ_EXTENSION = ".kmz"; -using SearchUserMarkContainer = SpecifiedUserMarkContainer; -using ApiUserMarkContainer = SpecifiedUserMarkContainer; -using DebugUserMarkContainer = SpecifiedUserMarkContainer; -using RouteUserMarkContainer = SpecifiedUserMarkContainer; -using LocalAdsMarkContainer = SpecifiedUserMarkContainer; -using TransitMarkContainer = SpecifiedUserMarkContainer; -using StaticUserMarkContainer = SpecifiedUserMarkContainer; - // Returns extension with a dot in a lower case. std::string const GetFileExt(std::string const & filePath) { @@ -58,13 +52,6 @@ std::string const GetFileName(std::string const & filePath) return ret; } -std::string const GenerateValidAndUniqueFilePathForKML(std::string const & fileName) -{ - std::string filePath = BookmarkManager::RemoveInvalidSymbols(fileName); - filePath = BookmarkManager::GenerateUniqueFileName(GetPlatform().SettingsDir(), filePath); - return filePath; -} - bool IsBadCharForPath(strings::UniChar const & c) { static strings::UniChar const illegalChars[] = {':', '/', '\\', '<', '>', '\"', '|', '?', '*'}; @@ -75,9 +62,39 @@ bool IsBadCharForPath(strings::UniChar const & c) return false; } -} // namespace -std::string BookmarkManager::RemoveInvalidSymbols(std::string const & name) +class FindMarkFunctor +{ +public: + FindMarkFunctor(UserMark const ** mark, double & minD, m2::AnyRectD const & rect) + : m_mark(mark) + , m_minD(minD) + , m_rect(rect) + { + m_globalCenter = rect.GlobalCenter(); + } + + void operator()(UserMark const * mark) + { + m2::PointD const & org = mark->GetPivot(); + if (m_rect.IsPointInside(org)) + { + double minDCandidate = m_globalCenter.SquareLength(org); + if (minDCandidate < m_minD) + { + *m_mark = mark; + m_minD = minDCandidate; + } + } + } + + UserMark const ** m_mark; + double & m_minD; + m2::AnyRectD const & m_rect; + m2::PointD m_globalCenter; +}; + +std::string RemoveInvalidSymbols(std::string const & name) { // Remove not allowed symbols strings::UniString uniName = strings::MakeUniString(name); @@ -85,7 +102,7 @@ std::string BookmarkManager::RemoveInvalidSymbols(std::string const & name) return (uniName.empty() ? "Bookmarks" : strings::ToUtf8(uniName)); } -std::string BookmarkManager::GenerateUniqueFileName(const std::string & path, std::string name) +std::string GenerateUniqueFileName(const std::string & path, std::string name) { std::string const kmlExt(BOOKMARKS_FILE_EXTENSION); @@ -107,26 +124,30 @@ std::string BookmarkManager::GenerateUniqueFileName(const std::string & path, st return (path + name + suffix + kmlExt); } +std::string const GenerateValidAndUniqueFilePathForKML(std::string const & fileName) +{ + std::string filePath = RemoveInvalidSymbols(fileName); + filePath = GenerateUniqueFileName(GetPlatform().SettingsDir(), filePath); + return filePath; +} + +} // namespace + BookmarkManager::BookmarkManager(Callbacks && callbacks) : m_callbacks(std::move(callbacks)) , m_bookmarksListeners(std::bind(&BookmarkManager::OnCreateUserMarks, this, _1, _2), std::bind(&BookmarkManager::OnUpdateUserMarks, this, _1, _2), std::bind(&BookmarkManager::OnDeleteUserMarks, this, _1, _2)) , m_needTeardown(false) - , m_nextCategoryId(UserMark::BOOKMARK) + , m_nextGroupID(UserMark::BOOKMARK) { ASSERT(m_callbacks.m_getStringsBundle != nullptr, ()); - m_userMarkLayers.resize(UserMark::PREDEFINED_COUNT); - m_userMarkLayers[UserMark::API] = my::make_unique(); - m_userMarkLayers[UserMark::SEARCH] = my::make_unique(); - m_userMarkLayers[UserMark::STATIC] = my::make_unique(); - m_userMarkLayers[UserMark::ROUTING] = my::make_unique(); - m_userMarkLayers[UserMark::TRANSIT] = my::make_unique(); - m_userMarkLayers[UserMark::LOCAL_ADS] = my::make_unique(); - m_userMarkLayers[UserMark::DEBUG_MARK] = my::make_unique(); + m_userMarkLayers.reserve(UserMark::BOOKMARK); + for (size_t i = 0; i < UserMark::BOOKMARK; ++i) + m_userMarkLayers.emplace_back(std::make_unique(static_cast(i))); - m_selectionMark = my::make_unique(); - m_myPositionMark = my::make_unique(); + m_selectionMark = CreateUserMark(m2::PointD{}); + m_myPositionMark = CreateUserMark(m2::PointD{}); } BookmarkManager::~BookmarkManager() @@ -135,99 +156,219 @@ BookmarkManager::~BookmarkManager() } //////////////////////////// -void BookmarkManager::NotifyChanges(size_t categoryId) + +UserMark const * BookmarkManager::GetUserMark(df::MarkID markID) const { - FindContainer(categoryId)->NotifyChanges(); + auto it = m_userMarks.find(markID); + return (it != m_userMarks.end()) ? it->second.get() : nullptr; } -size_t BookmarkManager::GetUserMarkCount(size_t categoryId) const +UserMark * BookmarkManager::GetUserMarkForEdit(df::MarkID markID) { - return FindContainer(categoryId)->GetUserMarkCount(); + auto it = m_userMarks.find(markID); + if (it == m_userMarks.end()) + return nullptr; + auto const groupId = static_cast(it->second->GetMarkType()); + m_userMarkLayers[groupId]->EditUserMark(markID); + return it->second.get(); } -UserMark const * BookmarkManager::GetUserMark(size_t categoryId, size_t index) const +void BookmarkManager::DeleteUserMark(df::MarkID markId) { - return FindContainer(categoryId)->GetUserMark(index); + auto it = m_userMarks.find(markId); + auto const groupId = it->second->GetGroupId(); + FindContainer(groupId)->DetachUserMark(markId); + m_userMarks.erase(it); } -UserMark * BookmarkManager::GetUserMarkForEdit(size_t categoryId, size_t index) +Bookmark * BookmarkManager::CreateBookmark(m2::PointD const & ptOrg, BookmarkData & bmData) { - return FindContainer(categoryId)->GetUserMarkForEdit(index); + return AddBookmark(std::make_unique(bmData, ptOrg)); } -void BookmarkManager::DeleteUserMark(size_t categoryId, size_t index) +Bookmark * BookmarkManager::CreateBookmark(m2::PointD const & ptOrg, BookmarkData & bm, df::MarkGroupID groupID) { - FindContainer(categoryId)->DeleteUserMark(index); + bm.SetTimeStamp(time(0)); + bm.SetScale(df::GetDrawTileScale(m_viewport)); + + auto * bookmark = CreateBookmark(ptOrg, bm); + bookmark->Attach(groupID); + auto * group = GetBmCategory(groupID); + group->AttachUserMark(bookmark->GetId()); + group->SetIsVisible(true); + SaveToKMLFile(groupID); + NotifyChanges(groupID); + + m_lastCategoryUrl = group->GetFileName(); + m_lastType = bm.GetType(); + SaveState(); + + return bookmark; } -void BookmarkManager::ClearUserMarks(size_t categoryId) +Bookmark const * BookmarkManager::GetBookmark(df::MarkID markID) const { - FindContainer(categoryId)->Clear(); + auto it = m_bookmarks.find(markID); + return (it != m_bookmarks.end()) ? it->second.get() : nullptr; } -Bookmark const * BookmarkManager::GetBookmark(size_t categoryId, size_t bmIndex) const +Bookmark * BookmarkManager::GetBookmarkForEdit(df::MarkID markID) { - ASSERT(categoryId >= UserMark::BOOKMARK, ()); - return static_cast(FindContainer(categoryId)->GetUserMark(bmIndex)); + auto it = m_bookmarks.find(markID); + if (it == m_bookmarks.end()) + return nullptr; + auto const groupId = it->second->GetGroupId(); + if (groupId) + GetBmCategory(groupId)->EditUserMark(markID); + return it->second.get(); } -Bookmark * BookmarkManager::GetBookmarkForEdit(size_t categoryId, size_t bmIndex) +void BookmarkManager::AttachBookmark(df::MarkID bmId, df::MarkGroupID catID) { - ASSERT(categoryId >= UserMark::BOOKMARK, ()); - return static_cast(FindContainer(categoryId)->GetUserMarkForEdit(bmIndex)); + GetBookmarkForEdit(bmId)->Attach(catID); + FindContainer(catID)->AttachUserMark(bmId); } -size_t BookmarkManager::GetTracksCount(size_t categoryId) const +void BookmarkManager::DetachBookmark(df::MarkID bmId, df::MarkGroupID catID) { - return GetBmCategory(categoryId)->GetTracksCount(); + GetBookmarkForEdit(bmId)->Detach(); + FindContainer(catID)->DetachUserMark(bmId); } -Track const * BookmarkManager::GetTrack(size_t categoryId, size_t index) const +void BookmarkManager::DeleteBookmark(df::MarkID bmId) { - return GetBmCategory(categoryId)->GetTrack(index); + auto it = m_bookmarks.find(bmId); + auto const groupID = it->second->GetGroupId(); + if (groupID) + FindContainer(groupID)->DetachUserMark(bmId); + m_bookmarks.erase(it); } -void BookmarkManager::DeleteTrack(size_t categoryId, size_t index) +Track * BookmarkManager::CreateTrack(m2::PolylineD const & polyline, Track::Params const & p) { - return GetBmCategory(categoryId)->DeleteTrack(index); + return AddTrack(std::make_unique(polyline, p)); } -bool BookmarkManager::SaveToKMLFile(size_t categoryId) +Track const * BookmarkManager::GetTrack(df::MarkID trackID) const { - return GetBmCategory(categoryId)->SaveToKMLFile(); + auto it = m_tracks.find(trackID); + return (it != m_tracks.end()) ? it->second.get() : nullptr; } -std::string const & BookmarkManager::GetCategoryName(size_t categoryId) const +void BookmarkManager::AttachTrack(df::MarkID trackID, df::MarkGroupID groupID) +{ + auto it = m_tracks.find(trackID); + it->second->Attach(groupID); + GetBmCategory(groupID)->AttachTrack(trackID); +} + +void BookmarkManager::DetachTrack(df::MarkID trackID, df::MarkGroupID groupID) +{ + GetBmCategory(groupID)->DetachTrack(trackID); +} + +void BookmarkManager::DeleteTrack(df::MarkID trackID) +{ + auto it = m_tracks.find(trackID); + auto const groupID = it->second->GetGroupId(); + if (groupID) + GetBmCategory(groupID)->DetachTrack(trackID); + m_tracks.erase(it); +} + +void BookmarkManager::NotifyChanges(df::MarkGroupID groupId) +{ + auto * group = FindContainer(groupId); + if (!group->IsDirty()) + return; + + group->NotifyListeners(); + + df::DrapeEngineLockGuard lock(m_drapeEngine); + if (!lock) + return; + + auto engine = lock.Get(); + + engine->ChangeVisibilityUserMarksGroup(groupId, group->IsVisible()); + + if (group->GetUserPointCount() == 0 && group->GetUserLineCount() == 0) + { + engine->UpdateUserMarksGroup(groupId, this); + engine->ClearUserMarksGroup(groupId); + } + else if (group->IsVisible()) + { + engine->UpdateUserMarksGroup(groupId, this); + } + + engine->InvalidateUserMarks(); +} + +BookmarkManager::MarkIDSet const & BookmarkManager::GetUserMarkIds(df::MarkGroupID groupID) const +{ + return FindContainer(groupID)->GetUserMarks(); +} + +BookmarkManager::MarkIDSet const & BookmarkManager::GetTrackIds(df::MarkGroupID groupID) const +{ + return GetBmCategory(groupID)->GetTracks(); +} + +void BookmarkManager::ClearUserMarks(df::MarkGroupID groupId) +{ + auto * group = FindContainer(groupId); + for (auto markId : group->GetUserMarks()) + { + if (IsBookmark(groupId)) + m_bookmarks.erase(markId); + else + m_userMarks.erase(markId); + } + group->Clear(); +} + +std::string const & BookmarkManager::GetCategoryName(df::MarkGroupID categoryId) const { return GetBmCategory(categoryId)->GetName(); } -void BookmarkManager::SetCategoryName(size_t categoryId, std::string const & name) +void BookmarkManager::SetCategoryName(df::MarkGroupID categoryId, std::string const & name) { GetBmCategory(categoryId)->SetName(name); } -std::string const & BookmarkManager::GetCategoryFileName(size_t categoryId) const +std::string const & BookmarkManager::GetCategoryFileName(df::MarkGroupID categoryId) const { return GetBmCategory(categoryId)->GetFileName(); } -UserMark const * BookmarkManager::FindMarkInRect(size_t categoryId, m2::AnyRectD const & rect, double & d) const +UserMark const * BookmarkManager::FindMarkInRect(df::MarkGroupID groupID, m2::AnyRectD const & rect, double & d) const { - return FindContainer(categoryId)->FindMarkInRect(rect, d); + auto const * group = FindContainer(groupID); + + UserMark const * mark = nullptr; + if (group->IsVisible()) + { + FindMarkFunctor f(&mark, d, rect); + for (auto markId : group->GetUserMarks()) + { + auto const * mark = IsBookmark(groupID) + ? static_cast(GetBookmark(markId)) + : GetUserMark(markId); + if (mark->IsAvailableForSearch() && rect.IsPointInside(mark->GetPivot())) + f(mark); + } + } + return mark; } -UserMark * BookmarkManager::CreateUserMark(size_t categoryId, m2::PointD const & ptOrg) -{ - return FindContainer(categoryId)->CreateUserMark(ptOrg); -} - -void BookmarkManager::SetIsVisible(size_t categoryId, bool visible) +void BookmarkManager::SetIsVisible(df::MarkGroupID categoryId, bool visible) { return FindContainer(categoryId)->SetIsVisible(visible); } -bool BookmarkManager::IsVisible(size_t categoryId) const +bool BookmarkManager::IsVisible(df::MarkGroupID categoryId) const { return FindContainer(categoryId)->IsVisible(); } @@ -237,12 +378,6 @@ bool BookmarkManager::IsVisible(size_t categoryId) const void BookmarkManager::SetDrapeEngine(ref_ptr engine) { m_drapeEngine.Set(engine); - - for (auto & userMarkLayer : m_userMarkLayers) - userMarkLayer->SetDrapeEngine(engine); - - for (auto & category : m_categories) - category.second->SetDrapeEngine(engine); } void BookmarkManager::UpdateViewport(ScreenBase const & screen) @@ -260,6 +395,24 @@ void BookmarkManager::Teardown() m_needTeardown = true; } +Bookmark * BookmarkManager::AddBookmark(std::unique_ptr && bookmark) +{ + auto * bm = bookmark.get(); + auto const markId = bm->GetId(); + ASSERT(m_bookmarks.count(markId) == 0, ()); + m_bookmarks.emplace(markId, std::move(bookmark)); + return bm; +} + +Track * BookmarkManager::AddTrack(std::unique_ptr && track) +{ + auto * t = track.get(); + auto const trackId = t->GetId(); + ASSERT(m_tracks.count(trackId) == 0, ()); + m_tracks.emplace(trackId, std::move(track)); + return t; +} + void BookmarkManager::SaveState() const { settings::Set(BOOKMARK_CATEGORY, m_lastCategoryUrl); @@ -275,7 +428,7 @@ void BookmarkManager::LoadState() void BookmarkManager::ClearCategories() { m_categories.clear(); - m_categoriesIdList.clear(); + m_bmGroupsIdList.clear(); } void BookmarkManager::LoadBookmarks() @@ -290,16 +443,16 @@ void BookmarkManager::LoadBookmarks() Platform::FilesList files; Platform::GetFilesByExt(dir, BOOKMARKS_FILE_EXTENSION, files); - auto collection = std::make_shared(); + auto collection = std::make_shared(); + collection->reserve(files.size()); for (auto const & file : files) { - size_t const id = m_nextCategoryId++; - auto cat = BookmarkCategory::CreateFromKMLFile(dir + file, id, m_bookmarksListeners); + auto kmlData = LoadKMLFile(dir + file); if (m_needTeardown) return; - if (cat != nullptr) - collection->emplace(id, std::move(cat)); + if (kmlData != nullptr) + collection->emplace_back(std::move(kmlData)); } NotifyAboutFinishAsyncLoading(std::move(collection)); }); @@ -323,7 +476,7 @@ void BookmarkManager::LoadBookmarkRoutine(std::string const & filePath, bool isT NotifyAboutStartAsyncLoading(); GetPlatform().RunTask(Platform::Thread::File, [this, filePath, isTemporaryFile]() { - auto collection = std::make_shared(); + auto collection = std::make_shared(); auto const fileSavePath = GetKMLPath(filePath); if (m_needTeardown) return; @@ -334,16 +487,15 @@ void BookmarkManager::LoadBookmarkRoutine(std::string const & filePath, bool isT } else { - auto const id = m_nextCategoryId++; - auto cat = BookmarkCategory::CreateFromKMLFile(fileSavePath.get(), id, m_bookmarksListeners); + auto kmlData = LoadKMLFile(fileSavePath.get()); if (m_needTeardown) return; - bool const categoryExists = (cat != nullptr); - if (categoryExists) - collection->emplace(id, std::move(cat)); + bool const dataExists = (kmlData != nullptr); + if (dataExists) + collection->emplace_back(std::move(kmlData)); - NotifyAboutFile(categoryExists, filePath, isTemporaryFile); + NotifyAboutFile(dataExists, filePath, isTemporaryFile); } NotifyAboutFinishAsyncLoading(std::move(collection)); }); @@ -362,7 +514,7 @@ void BookmarkManager::NotifyAboutStartAsyncLoading() }); } -void BookmarkManager::NotifyAboutFinishAsyncLoading(std::shared_ptr && collection) +void BookmarkManager::NotifyAboutFinishAsyncLoading(std::shared_ptr && collection) { if (m_needTeardown) return; @@ -372,7 +524,7 @@ void BookmarkManager::NotifyAboutFinishAsyncLoading(std::shared_ptrempty()) - MergeCategories(std::move(*collection)); + CreateCategories(std::move(*collection)); if (m_asyncLoadingCallbacks.m_onFinished != nullptr) m_asyncLoadingCallbacks.m_onFinished(); @@ -455,53 +607,24 @@ boost::optional BookmarkManager::GetKMLPath(std::string const & fil void BookmarkManager::InitBookmarks() { for (auto & cat : m_categories) - cat.second->NotifyChanges(); + NotifyChanges(cat.first); } -size_t BookmarkManager::AddBookmark(size_t categoryId, m2::PointD const & ptOrg, BookmarkData & bm) +void BookmarkManager::MoveBookmark(df::MarkID bmID, df::MarkGroupID curGroupID, df::MarkGroupID newGroupID) { - bm.SetTimeStamp(time(0)); - bm.SetScale(df::GetDrawTileScale(m_viewport)); + DetachBookmark(bmID, curGroupID); + SaveToKMLFile(curGroupID); + NotifyChanges(curGroupID); + AttachBookmark(bmID, newGroupID); +} - BookmarkCategory * cat = GetBmCategory(categoryId); - - auto bookmark = static_cast(cat->CreateUserMark(ptOrg)); +void BookmarkManager::UpdateBookmark(df::MarkID bmID, BookmarkData const & bm) +{ + auto * bookmark = GetBookmarkForEdit(bmID); bookmark->SetData(bm); - cat->SetIsVisible(true); - cat->SaveToKMLFile(); - cat->NotifyChanges(); - - m_lastCategoryUrl = cat->GetFileName(); - m_lastType = bm.GetType(); - SaveState(); - - // Bookmark always is pushed front. - return 0; -} - -size_t BookmarkManager::MoveBookmark(size_t bmIndex, size_t curCatId, size_t newCatId) -{ - BookmarkData data; - m2::PointD ptOrg; - - BookmarkCategory * cat = GetBmCategory(curCatId); - auto bm = static_cast(cat->GetUserMark(bmIndex)); - data = bm->GetData(); - ptOrg = bm->GetPivot(); - - cat->DeleteUserMark(bmIndex); - cat->SaveToKMLFile(); - cat->NotifyChanges(); - - return AddBookmark(newCatId, ptOrg, data); -} - -void BookmarkManager::ReplaceBookmark(size_t categoryId, size_t bmIndex, BookmarkData const & bm) -{ - BookmarkCategory * cat = GetBmCategory(categoryId); - static_cast(cat->GetUserMarkForEdit(bmIndex))->SetData(bm); - cat->SaveToKMLFile(); - cat->NotifyChanges(); + ASSERT(bookmark->GetGroupId(), ()); + SaveToKMLFile(bookmark->GetGroupId()); + NotifyChanges(bookmark->GetGroupId()); m_lastType = bm.GetType(); SaveState(); @@ -518,7 +641,7 @@ size_t BookmarkManager::LastEditedBMCategory() if (m_categories.empty()) CreateBmCategory(m_callbacks.m_getStringsBundle().GetString("my_places")); - return m_categoriesIdList.front(); + return m_bmGroupsIdList.front(); } std::string BookmarkManager::LastEditedBMType() const @@ -526,7 +649,7 @@ std::string BookmarkManager::LastEditedBMType() const return (m_lastType.empty() ? BookmarkCategory::GetDefaultType() : m_lastType); } -BookmarkCategory * BookmarkManager::GetBmCategory(size_t categoryId) const +BookmarkCategory * BookmarkManager::GetBmCategory(df::MarkGroupID categoryId) const { ASSERT(categoryId >= UserMark::BOOKMARK, ()); auto const it = m_categories.find(categoryId); @@ -542,7 +665,7 @@ void BookmarkManager::OnCreateUserMarks(UserMarkContainer const & container, df: return; std::vector> marksInfo; - GetBookmarksData(container, markIds, marksInfo); + GetBookmarksData(markIds, marksInfo); m_callbacks.m_createdBookmarksCallback(marksInfo); } @@ -556,7 +679,7 @@ void BookmarkManager::OnUpdateUserMarks(UserMarkContainer const & container, df: return; std::vector> marksInfo; - GetBookmarksData(container, markIds, marksInfo); + GetBookmarksData(markIds, marksInfo); m_callbacks.m_updatedBookmarksCallback(marksInfo); } @@ -572,54 +695,52 @@ void BookmarkManager::OnDeleteUserMarks(UserMarkContainer const & container, df: m_callbacks.m_deletedBookmarksCallback(markIds); } -void BookmarkManager::GetBookmarksData(UserMarkContainer const & container, df::IDCollection const & markIds, +void BookmarkManager::GetBookmarksData(df::IDCollection const & markIds, std::vector> & data) const { data.clear(); 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); + auto const * bookmark = GetBookmark(markId); + ASSERT(bookmark != nullptr, ()); data.push_back(std::make_pair(markId, bookmark->GetData())); } } -bool BookmarkManager::HasBmCategory(size_t categoryId) const +bool BookmarkManager::HasBmCategory(df::MarkGroupID groupID) const { - return m_categories.find(categoryId) != m_categories.end(); + return m_categories.find(groupID) != m_categories.end(); } -size_t BookmarkManager::CreateBmCategory(std::string const & name) +df::MarkGroupID BookmarkManager::CreateBmCategory(std::string const & name) { - size_t const id = m_nextCategoryId++; - - auto & cat = m_categories[id]; - cat = my::make_unique(name, id, m_bookmarksListeners); - m_categoriesIdList.push_back(id); - - df::DrapeEngineLockGuard lock(m_drapeEngine); - if (lock) - cat->SetDrapeEngine(lock.Get()); - - return id; + auto const groupId = m_nextGroupID++; + auto & cat = m_categories[groupId]; + cat = my::make_unique(name, groupId, m_bookmarksListeners); + m_bmGroupsIdList.push_back(groupId); + return groupId; } -bool BookmarkManager::DeleteBmCategory(size_t categoryId) +bool BookmarkManager::DeleteBmCategory(df::MarkGroupID groupID) { - auto it = m_categories.find(categoryId); + auto it = m_categories.find(groupID); if (it == m_categories.end()) return false; - BookmarkCategory & cat = *it->second.get(); - cat.DeleteLater(); - FileWriter::DeleteFileX(cat.GetFileName()); + BookmarkCategory & group = *it->second.get(); + // TODO(darina): check the necessity of DeleteLater + // cat.DeleteLater(); + FileWriter::DeleteFileX(group.GetFileName()); + for (auto markId : group.GetUserMarks()) + m_bookmarks.erase(markId); + for (auto trackId : group.GetTracks()) + m_tracks.erase(trackId); + it->second->Clear(); + NotifyChanges(groupID); m_categories.erase(it); - m_categoriesIdList.erase(std::remove(m_categoriesIdList.begin(), m_categoriesIdList.end(), categoryId), - m_categoriesIdList.end()); + m_bmGroupsIdList.erase(std::remove(m_bmGroupsIdList.begin(), m_bmGroupsIdList.end(), groupID), + m_bmGroupsIdList.end()); return true; } @@ -635,10 +756,10 @@ public: , m_manager(manager) {} - void operator()(size_t categoryId) + void operator()(df::MarkGroupID groupID) { - m2::AnyRectD const & rect = m_rectHolder(min((UserMark::Type)categoryId, UserMark::BOOKMARK)); - if (UserMark const * p = m_manager->FindMarkInRect(categoryId, rect, m_d)) + m2::AnyRectD const & rect = m_rectHolder(min((UserMark::Type)groupID, UserMark::BOOKMARK)); + if (UserMark const * p = m_manager->FindMarkInRect(groupID, rect, m_d)) { static double const kEps = 1e-5; if (m_mark == nullptr || !p->GetPivot().EqualDxDy(m_mark->GetPivot(), kEps)) @@ -656,38 +777,6 @@ private: }; } // namespace -Bookmark const * BookmarkManager::GetBookmark(df::MarkID id) const -{ - for (auto const & category : m_categories) - { - auto const mark = category.second->GetUserMarkById(id); - if (mark != nullptr) - { - ASSERT(dynamic_cast(mark) != nullptr, ()); - return static_cast(mark); - } - } - return nullptr; -} - -Bookmark const * BookmarkManager::GetBookmark(df::MarkID id, size_t & catIndex, size_t & bmIndex) const -{ - size_t index = 0; - UserMark const * mark = nullptr; - for (auto & it : m_categories) - { - mark = it.second->GetUserMarkById(id, index); - if (mark != nullptr) - { - catIndex = it.first; - bmIndex = index; - ASSERT(dynamic_cast(mark) != nullptr, ()); - return static_cast(mark); - } - } - return nullptr; -} - UserMark const * BookmarkManager::FindNearestUserMark(m2::AnyRectD const & rect) const { return FindNearestUserMark([&rect](UserMark::Type) { return rect; }); @@ -699,13 +788,13 @@ UserMark const * BookmarkManager::FindNearestUserMark(TTouchRectHolder const & h finder(UserMark::Type::ROUTING); finder(UserMark::Type::SEARCH); finder(UserMark::Type::API); - for (auto & it : m_categories) - finder(it.first); + for (auto & pair : m_categories) + finder(pair.first); return finder.GetFoundMark(); } -UserMarkContainer const * BookmarkManager::FindContainer(size_t containerId) const +UserMarkContainer const * BookmarkManager::FindContainer(df::MarkGroupID containerId) const { if (containerId < UserMark::Type::BOOKMARK) return m_userMarkLayers[containerId].get(); @@ -716,7 +805,7 @@ UserMarkContainer const * BookmarkManager::FindContainer(size_t containerId) con } } -UserMarkContainer * BookmarkManager::FindContainer(size_t containerId) +UserMarkContainer * BookmarkManager::FindContainer(df::MarkGroupID containerId) { if (containerId < UserMark::Type::BOOKMARK) return m_userMarkLayers[containerId].get(); @@ -727,75 +816,331 @@ UserMarkContainer * BookmarkManager::FindContainer(size_t containerId) } } -std::unique_ptr & BookmarkManager::SelectionMark() +void BookmarkManager::AcceptChanges(df::MarkGroupID groupID, + df::MarkIDCollection & groupMarks, + df::MarkIDCollection & createdMarks, + df::MarkIDCollection & removedMarks) { - ASSERT(m_selectionMark != nullptr, ()); - return m_selectionMark; + FindContainer(groupID)->AcceptChanges(groupMarks, createdMarks, removedMarks); } -std::unique_ptr & BookmarkManager::MyPositionMark() +df::UserPointMark const * BookmarkManager::GetUserPointMark(df::MarkID markID) const { - ASSERT(m_myPositionMark != nullptr, ()); - return m_myPositionMark; + df::UserPointMark const * mark = GetUserMark(markID); + if (!mark) + mark = GetBookmark(markID); + return mark; } -std::unique_ptr const & BookmarkManager::SelectionMark() const +df::UserLineMark const * BookmarkManager::GetUserLineMark(df::MarkID markID) const { - ASSERT(m_selectionMark != nullptr, ()); - return m_selectionMark; + return GetTrack(markID); } -std::unique_ptr const & BookmarkManager::MyPositionMark() const +void BookmarkManager::CreateCategories(KMLDataCollection && dataCollection) { - ASSERT(m_myPositionMark != nullptr, ()); - return m_myPositionMark; -} - -void BookmarkManager::MergeCategories(CategoriesCollection && newCategories) -{ - for (auto & category : m_categories) + for (auto & data : dataCollection) { - // Since all KML-files are being loaded asynchronously user can create - // new category during loading. So we have to merge categories after loading. - std::string const categoryName = category.second->GetName(); - auto const it = std::find_if(newCategories.begin(), newCategories.end(), - [&categoryName](CategoriesCollection::value_type const & v) + df::MarkGroupID groupID; + BookmarkCategory * group = nullptr; + + auto const it = std::find_if(m_categories.begin(), m_categories.end(), + [&data](CategoriesCollection::value_type const & v) { - return v.second->GetName() == categoryName; + return v.second->GetName() == data->m_name; }); - if (it == newCategories.end()) - continue; - auto * existingCat = category.second.get(); - auto * newCat = it->second.get(); - - // Copy bookmarks and tracks to the existing category. - for (size_t i = 0, sz = newCat->GetUserMarkCount(); i < sz; ++i) + bool merge = it != m_categories.end(); + if (merge) { - auto srcBookmark = static_cast(newCat->GetUserMark(i)); - auto bookmark = static_cast(existingCat->CreateUserMark(srcBookmark->GetPivot())); - bookmark->SetData(srcBookmark->GetData()); + groupID = it->first; + group = it->second.get(); } - existingCat->AppendTracks(newCat->StealTracks()); - existingCat->SaveToKMLFile(); - - // Delete file since it has been merged. - my::DeleteFileX(newCat->GetFileName()); - - newCategories.erase(it); - } - - for (auto & category : newCategories) - m_categoriesIdList.push_back(category.first); - m_categories.insert(make_move_iterator(newCategories.begin()), - make_move_iterator(newCategories.end())); - - df::DrapeEngineLockGuard lock(m_drapeEngine); - if (lock) - { - for (auto & cat : m_categories) + else { - cat.second->SetDrapeEngine(lock.Get()); - cat.second->NotifyChanges(); + groupID = CreateBmCategory(data->m_name); + group = GetBmCategory(groupID); + group->SetFileName(data->m_file); + group->SetIsVisible(data->m_visible); + } + for (auto & bookmark : data->m_bookmarks) + { + auto * bm = AddBookmark(std::move(bookmark)); + bm->Attach(groupID); + group->AttachUserMark(bm->GetId()); + } + for (auto & track : data->m_tracks) + { + auto * t = AddTrack(std::move(track)); + t->Attach(groupID); + group->AttachTrack(t->GetId()); + } + if (merge) + { + SaveToKMLFile(groupID); + // Delete file since it has been merged. + // TODO(darina): why not delete the file before saving it? + my::DeleteFileX(data->m_file); } } + + for (auto & cat : m_categories) + NotifyChanges(cat.first); +} + + + + +namespace +{ +char const * kmlHeader = + "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" +; + +char const * kmlFooter = + "\n" + "\n"; +} + +namespace +{ +inline void SaveStringWithCDATA(std::ostream & stream, std::string const & s) +{ + // According to kml/xml spec, we need to escape special symbols with CDATA + if (s.find_first_of("<&") != std::string::npos) + stream << ""; + else + stream << s; +} + +std::string PointToString(m2::PointD const & org) +{ + double const lon = MercatorBounds::XToLon(org.x); + double const lat = MercatorBounds::YToLat(org.y); + + ostringstream ss; + ss.precision(8); + + ss << lon << "," << lat; + return ss.str(); +} +} + +void BookmarkManager::SaveToKML(BookmarkCategory * group, std::ostream & s) +{ + s << kmlHeader; + + // Use CDATA if we have special symbols in the name + s << " "; + SaveStringWithCDATA(s, group->GetName()); + s << "\n"; + + s << " " << (group->IsVisible() ? "1" : "0") <<"\n"; + + for (auto markId : group->GetUserMarks()) + { + Bookmark const * bm = GetBookmark(markId); + s << " \n"; + s << " "; + SaveStringWithCDATA(s, bm->GetName()); + s << "\n"; + + if (!bm->GetDescription().empty()) + { + s << " "; + SaveStringWithCDATA(s, bm->GetDescription()); + s << "\n"; + } + + time_t const timeStamp = bm->GetTimeStamp(); + if (timeStamp != my::INVALID_TIME_STAMP) + { + std::string const strTimeStamp = my::TimestampToString(timeStamp); + ASSERT_EQUAL(strTimeStamp.size(), 20, ("We always generate fixed length UTC-format timestamp")); + s << " " << strTimeStamp << "\n"; + } + + s << " #" << bm->GetType() << "\n" + << " " << PointToString(bm->GetPivot()) << "\n"; + + double const scale = bm->GetScale(); + if (scale != -1.0) + { + /// @todo Factor out to separate function to use for other custom params. + s << " \n" + << " " << bm->GetScale() << "\n" + << " \n"; + } + + s << " \n"; + } + + // Saving tracks + for (auto trackId : group->GetTracks()) + { + Track const * track = GetTrack(trackId); + + s << " \n"; + s << " "; + SaveStringWithCDATA(s, track->GetName()); + s << "\n"; + + ASSERT_GREATER(track->GetLayerCount(), 0, ()); + + s << "\n"; + // stop style saving + + s << " "; + + Track::PolylineD const & poly = track->GetPolyline(); + for (auto pt = poly.Begin(); pt != poly.End(); ++pt) + s << PointToString(*pt) << " "; + + s << " \n" + << " \n"; + } + + s << kmlFooter; +} + +bool BookmarkManager::SaveToKMLFile(df::MarkGroupID groupID) +{ + std::string oldFile; + + auto * group = GetBmCategory(groupID); + + // Get valid file name from category name + std::string const name = RemoveInvalidSymbols(group->GetName()); + std::string file = group->GetFileName(); + + if (!file.empty()) + { + size_t i2 = file.find_last_of('.'); + if (i2 == std::string::npos) + i2 = file.size(); + size_t i1 = file.find_last_of("\\/"); + if (i1 == std::string::npos) + i1 = 0; + else + ++i1; + + // If m_file doesn't match name, assign new m_file for this category and save old file name. + if (file.substr(i1, i2 - i1).find(name) != 0) + { + file.swap(oldFile); + } + } + if (file.empty()) + { + file = GenerateUniqueFileName(GetPlatform().SettingsDir(), name); + group->SetFileName(file); + } + + std::string const fileTmp = file + ".tmp"; + + try + { + // First, we save to the temporary file + /// @todo On Windows UTF-8 file names are not supported. + std::ofstream of(fileTmp.c_str(), std::ios_base::out | std::ios_base::trunc); + SaveToKML(group, of); + of.flush(); + + if (!of.fail()) + { + // Only after successfull save we replace original file + my::DeleteFileX(file); + VERIFY(my::RenameFileX(fileTmp, file), (fileTmp, file)); + // delete old file + if (!oldFile.empty()) + VERIFY(my::DeleteFileX(oldFile), (oldFile, file)); + + return true; + } + } + catch (std::exception const & e) + { + LOG(LWARNING, ("Exception while saving bookmarks:", e.what())); + } + + LOG(LWARNING, ("Can't save bookmarks category", name, "to file", file)); + + // remove possibly left tmp file + my::DeleteFileX(fileTmp); + + // return old file name in case of error + if (!oldFile.empty()) + group->SetFileName(oldFile); + + return false; } diff --git a/map/bookmark_manager.hpp b/map/bookmark_manager.hpp index 30d7f02a8a..71e1c40278 100644 --- a/map/bookmark_manager.hpp +++ b/map/bookmark_manager.hpp @@ -20,11 +20,16 @@ #include -class BookmarkManager final +class BookmarkManager final : public df::UserMarksProvider { - using CategoriesCollection = std::map>; + using CategoriesCollection = std::map>; + using MarksCollection = std::map>; + using BookmarksCollection = std::map>; + using TracksCollection = std::map>; + using CategoryIter = CategoriesCollection::iterator; - using CategoriesIdList = std::vector; + using GroupIdList = std::vector; + using MarkIDSet = UserMarkContainer::MarkIDSet; using UserMarkLayers = std::vector>; public: @@ -66,34 +71,92 @@ public: explicit BookmarkManager(Callbacks && callbacks); ~BookmarkManager(); + template + UserMarkT * CreateUserMark(m2::PointD const & ptOrg) + { + auto mark = std::make_unique(ptOrg); + auto * m = mark.get(); + auto const markId = m->GetId(); + auto const groupId = static_cast(m->GetMarkType()); + ASSERT(m_userMarks.count(markId) == 0, ()); + ASSERT_LESS(groupId, m_userMarkLayers.size(), ()); + m_userMarks.emplace(markId, std::move(mark)); + m_userMarkLayers[groupId]->AttachUserMark(markId); + return m; + } + + template + UserMarkT * GetMarkForEdit(df::MarkID markId) + { + auto * mark = GetUserMarkForEdit(markId); + ASSERT(dynamic_cast(mark) != nullptr, ()); + return static_cast(mark); + } + + template + UserMarkT const * GetMark(df::MarkID markId) const + { + auto * mark = GetUserMark(markId); + ASSERT(dynamic_cast(mark) != nullptr, ()); + return static_cast(mark); + } + + template + void DeleteUserMarks(UserMark::Type type, F deletePredicate) + { + std::list marksToDelete; + for (auto markId : GetUserMarkIds(type)) + { + if (deletePredicate(GetMark(markId))) + marksToDelete.push_back(markId); + } + // Delete after iterating to avoid iterators invalidation issues. + for (auto markId : marksToDelete) + DeleteUserMark(markId); + }; + + UserMark const * GetUserMark(df::MarkID markID) const; + UserMark * GetUserMarkForEdit(df::MarkID markID); + void DeleteUserMark(df::MarkID markId); + + Bookmark * CreateBookmark(m2::PointD const & ptOrg, BookmarkData & bm); + Bookmark * CreateBookmark(m2::PointD const & ptOrg, BookmarkData & bm, df::MarkGroupID groupID); + Bookmark const * GetBookmark(df::MarkID markID) const; + Bookmark * GetBookmarkForEdit(df::MarkID markID); + void AttachBookmark(df::MarkID bmId, df::MarkGroupID groupID); + void DetachBookmark(df::MarkID bmId, df::MarkGroupID groupID); + void DeleteBookmark(df::MarkID bmId); + + Track * CreateTrack(m2::PolylineD const & polyline, Track::Params const & p); + Track const * GetTrack(df::MarkID trackID) const; + void AttachTrack(df::MarkID trackID, df::MarkGroupID groupID); + void DetachTrack(df::MarkID trackID, df::MarkGroupID groupID); + void DeleteTrack(df::MarkID trackID); + ////////////////// - void NotifyChanges(size_t categoryId); - size_t GetUserMarkCount(size_t categoryId) const; - UserMark const * GetUserMark(size_t categoryId, size_t index) const; - UserMark * GetUserMarkForEdit(size_t categoryId, size_t index); - void DeleteUserMark(size_t categoryId, size_t index); - void ClearUserMarks(size_t categoryId); - Bookmark const * GetBookmark(size_t categoryId, size_t bmIndex) const; - Bookmark * GetBookmarkForEdit(size_t categoryId, size_t bmIndex); - size_t GetTracksCount(size_t categoryId) const; - Track const * GetTrack(size_t categoryId, size_t index) const; - void DeleteTrack(size_t categoryId, size_t index); - bool SaveToKMLFile(size_t categoryId); - std::string const & GetCategoryName(size_t categoryId) const; - void SetCategoryName(size_t categoryId, std::string const & name); - std::string const & GetCategoryFileName(size_t categoryId) const; + void ClearUserMarks(df::MarkGroupID groupID); - UserMark const * FindMarkInRect(size_t categoryId, m2::AnyRectD const & rect, double & d) const; + void NotifyChanges(df::MarkGroupID groupID); - UserMark * CreateUserMark(size_t categoryId, m2::PointD const & ptOrg); + MarkIDSet const & GetUserMarkIds(df::MarkGroupID groupID) const; + MarkIDSet const & GetTrackIds(df::MarkGroupID groupID) const; - void SetIsVisible(size_t categoryId, bool visible); - bool IsVisible(size_t categoryId) const; + std::string const & GetCategoryName(df::MarkGroupID categoryId) const; + void SetCategoryName(df::MarkGroupID categoryId, std::string const & name); - /// Get valid file name from input (remove illegal symbols). - static std::string RemoveInvalidSymbols(std::string const & name); - /// Get unique bookmark file name from path and valid file name. - static std::string GenerateUniqueFileName(const std::string & path, std::string name); + UserMark const * FindMarkInRect(df::MarkGroupID categoryId, m2::AnyRectD const & rect, double & d) const; + + void SetIsVisible(df::MarkGroupID categoryId, bool visible); + bool IsVisible(df::MarkGroupID categoryId) const; + + /// Uses the same file name from which was loaded, or + /// creates unique file name on first save and uses it every time. + bool SaveToKMLFile(df::MarkGroupID groupID); + /// @name This fuctions is public for unit tests only. + /// You don't need to call it from client code. + void SaveToKML(BookmarkCategory * group, std::ostream & s); + + std::string const & GetCategoryFileName(df::MarkGroupID categoryId) const; ////////////////// void SetDrapeEngine(ref_ptr engine); @@ -109,49 +172,58 @@ public: void InitBookmarks(); - /// Client should know where it adds bookmark - size_t AddBookmark(size_t categoryIndex, m2::PointD const & ptOrg, BookmarkData & bm); /// Client should know where it moves bookmark - size_t MoveBookmark(size_t bmIndex, size_t curCatIndex, size_t newCatIndex); - void ReplaceBookmark(size_t catIndex, size_t bmIndex, BookmarkData const & bm); + void MoveBookmark(df::MarkID bmID, df::MarkGroupID curGroupID, df::MarkGroupID newGroupID); + void UpdateBookmark(df::MarkID bmId, BookmarkData const & bm); - size_t LastEditedBMCategory(); + df::MarkGroupID LastEditedBMCategory(); std::string LastEditedBMType() const; - CategoriesIdList const & GetBmCategoriesIds() const { return m_categoriesIdList; } - bool HasBmCategory(size_t categoryId) const; + GroupIdList const & GetBmGroupsIdList() const { return m_bmGroupsIdList; } + bool HasBmCategory(df::MarkGroupID groupID) const; - size_t CreateBmCategory(std::string const & name); + df::MarkGroupID CreateBmCategory(std::string const & name); /// @name Delete bookmarks category with all bookmarks. /// @return true if category was deleted - bool DeleteBmCategory(size_t categoryId); + bool DeleteBmCategory(df::MarkGroupID groupID); using TTouchRectHolder = function; - Bookmark const * GetBookmark(df::MarkID id) const; - Bookmark const * GetBookmark(df::MarkID id, size_t & catIndex, size_t & bmIndex) const; - UserMark const * FindNearestUserMark(m2::AnyRectD const & rect) const; UserMark const * FindNearestUserMark(TTouchRectHolder const & holder) const; - std::unique_ptr & SelectionMark(); - std::unique_ptr const & SelectionMark() const; - std::unique_ptr & MyPositionMark(); - std::unique_ptr const & MyPositionMark() const; + StaticMarkPoint & SelectionMark() { return *m_selectionMark; } + StaticMarkPoint const & SelectionMark() const { return *m_selectionMark; } + MyPositionMarkPoint & MyPositionMark() { return *m_myPositionMark; } + MyPositionMarkPoint const & MyPositionMark() const { return *m_myPositionMark; } bool IsAsyncLoadingInProgress() const { return m_asyncLoadingInProgress; } + void AcceptChanges(df::MarkGroupID groupID, + df::MarkIDCollection & groupMarks, + df::MarkIDCollection & createdMarks, + df::MarkIDCollection & removedMarks) override; + df::UserPointMark const * GetUserPointMark(df::MarkID markID) const override; + df::UserLineMark const * GetUserLineMark(df::MarkID markID) const override; + private: - UserMarkContainer const * FindContainer(size_t containerId) const; - UserMarkContainer * FindContainer(size_t containerId); - BookmarkCategory * GetBmCategory(size_t categoryId) const; + using KMLDataCollection = std::vector>; + + bool IsBookmark(df::MarkGroupID groupId) const { return groupId >= UserMark::BOOKMARK; } + + UserMarkContainer const * FindContainer(df::MarkGroupID containerId) const; + UserMarkContainer * FindContainer(df::MarkGroupID containerId); + BookmarkCategory * GetBmCategory(df::MarkGroupID categoryId) const; + + Bookmark * AddBookmark(std::unique_ptr && bookmark); + Track * AddTrack(std::unique_ptr && track); void SaveState() const; void LoadState(); - void MergeCategories(CategoriesCollection && newCategories); + void CreateCategories(KMLDataCollection && dataCollection); void NotifyAboutStartAsyncLoading(); - void NotifyAboutFinishAsyncLoading(std::shared_ptr && collection); + void NotifyAboutFinishAsyncLoading(std::shared_ptr && collection); boost::optional GetKMLPath(std::string const & filePath); void NotifyAboutFile(bool success, std::string const & filePath, bool isTemporaryFile); void LoadBookmarkRoutine(std::string const & filePath, bool isTemporaryFile); @@ -159,7 +231,7 @@ private: void OnCreateUserMarks(UserMarkContainer const & container, df::IDCollection const & markIds); void OnUpdateUserMarks(UserMarkContainer const & container, df::IDCollection const & markIds); void OnDeleteUserMarks(UserMarkContainer const & container, df::IDCollection const & markIds); - void GetBookmarksData(UserMarkContainer const & container, df::IDCollection const & markIds, + void GetBookmarksData(df::IDCollection const & markIds, std::vector> & data) const; Callbacks m_callbacks; @@ -168,19 +240,25 @@ private: df::DrapeEngineSafePtr m_drapeEngine; AsyncLoadingCallbacks m_asyncLoadingCallbacks; std::atomic m_needTeardown; - std::atomic m_nextCategoryId; + df::MarkGroupID m_nextGroupID; bool m_loadBookmarksFinished = false; ScreenBase m_viewport; CategoriesCollection m_categories; - CategoriesIdList m_categoriesIdList; + GroupIdList m_bmGroupsIdList; std::string m_lastCategoryUrl; std::string m_lastType; UserMarkLayers m_userMarkLayers; - std::unique_ptr m_selectionMark; - std::unique_ptr m_myPositionMark; + uint64_t m_generation; + + MarksCollection m_userMarks; + BookmarksCollection m_bookmarks; + TracksCollection m_tracks; + + StaticMarkPoint* m_selectionMark; + MyPositionMarkPoint* m_myPositionMark; bool m_asyncLoadingInProgress = false; struct BookmarkLoaderInfo diff --git a/map/framework.cpp b/map/framework.cpp index 1a8bf0837b..e880cd867f 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -257,7 +257,7 @@ LocalAdsManager & Framework::GetLocalAdsManager() void Framework::OnUserPositionChanged(m2::PointD const & position, bool hasPosition) { - GetBookmarkManager().MyPositionMark()->SetUserPosition(position, hasPosition); + GetBookmarkManager().MyPositionMark().SetUserPosition(position, hasPosition); m_routingManager.SetUserCurrentPosition(position); m_trafficManager.UpdateMyPosition(TrafficManager::MyPosition(position)); } @@ -733,24 +733,24 @@ void Framework::LoadBookmarks() GetBookmarkManager().LoadBookmarks(); } -size_t Framework::AddBookmark(size_t categoryIndex, const m2::PointD & ptOrg, BookmarkData & bm) +df::MarkID Framework::AddBookmark(df::MarkGroupID catId, const m2::PointD & ptOrg, BookmarkData & bm) { GetPlatform().GetMarketingService().SendMarketingEvent(marketing::kBookmarksBookmarkAction, {{"action", "create"}}); - return GetBookmarkManager().AddBookmark(categoryIndex, ptOrg, bm); + return GetBookmarkManager().CreateBookmark(ptOrg, bm, catId)->GetId(); } -size_t Framework::MoveBookmark(size_t bmIndex, size_t curCatIndex, size_t newCatIndex) +void Framework::MoveBookmark(df::MarkID bmId, df::MarkGroupID curCatId, df::MarkGroupID newCatId) { - return GetBookmarkManager().MoveBookmark(bmIndex, curCatIndex, newCatIndex); + return GetBookmarkManager().MoveBookmark(bmId, curCatId, newCatId); } -void Framework::ReplaceBookmark(size_t catIndex, size_t bmIndex, BookmarkData const & bm) +void Framework::ReplaceBookmark(df::MarkID bmId, BookmarkData const & bm) { - GetBookmarkManager().ReplaceBookmark(catIndex, bmIndex, bm); + GetBookmarkManager().UpdateBookmark(bmId, bm); } -size_t Framework::AddCategory(string const & categoryName) +df::MarkGroupID Framework::AddCategory(string const & categoryName) { return GetBookmarkManager().CreateBmCategory(categoryName); } @@ -760,12 +760,12 @@ bool Framework::DeleteBmCategory(size_t index) return GetBookmarkManager().DeleteBmCategory(index); } -void Framework::FillBookmarkInfo(Bookmark const & bmk, BookmarkAndCategory const & bac, place_page::Info & info) const +void Framework::FillBookmarkInfo(Bookmark const & bmk, place_page::Info & info) const { - info.SetBookmarkCategoryName(GetBookmarkManager().GetCategoryName(bac.m_categoryIndex)); - BookmarkData const & data = GetBookmarkManager().GetBookmark(bac.m_categoryIndex, bac.m_bookmarkIndex)->GetData(); - info.SetBookmarkData(data); - info.SetBac(bac); + info.SetBookmarkCategoryName(GetBookmarkManager().GetCategoryName(bmk.GetGroupId())); + info.SetBookmarkData(bmk.GetData()); + info.SetBookmarkId(bmk.GetId()); + info.SetBookmarkCategoryId(bmk.GetGroupId()); FillPointInfo(bmk.GetPivot(), {} /* customTitle */, info); } @@ -773,7 +773,8 @@ void Framework::ResetBookmarkInfo(Bookmark const & bmk, place_page::Info & info) { info.SetBookmarkCategoryName(""); info.SetBookmarkData({}); - info.SetBac({}); + info.SetBookmarkId(0); + info.SetBookmarkCategoryId(0); FillPointInfo(bmk.GetPivot(), {} /* customTitle */, info); } @@ -989,18 +990,11 @@ void Framework::FillRouteMarkInfo(RouteMarkPoint const & rmp, place_page::Info & void Framework::ShowBookmark(df::MarkID id) { - BookmarkAndCategory bnc; - auto const mark = m_bmManager->GetBookmark(id, bnc.m_categoryIndex, bnc.m_bookmarkIndex); - ShowBookmark(mark, bnc); + auto const * mark = m_bmManager->GetBookmark(id); + ShowBookmark(mark); } -void Framework::ShowBookmark(BookmarkAndCategory const & bnc) -{ - auto const bookmark = GetBookmarkManager().GetBookmark(bnc.m_categoryIndex, bnc.m_bookmarkIndex); - ShowBookmark(bookmark, bnc); -} - -void Framework::ShowBookmark(Bookmark const * mark, BookmarkAndCategory const & bnc) +void Framework::ShowBookmark(Bookmark const * mark) { if (mark == nullptr) return; @@ -1016,7 +1010,7 @@ void Framework::ShowBookmark(Bookmark const * mark, BookmarkAndCategory const & true /* trackVisibleViewport */); place_page::Info info; - FillBookmarkInfo(*mark, bnc, info); + FillBookmarkInfo(*mark, info); ActivateMapSelection(true, df::SelectionShape::OBJECT_USER_MARK, info); // TODO // We need to preserve bookmark id in the m_lastTapEvent, because one feature can have several bookmarks. @@ -1044,7 +1038,7 @@ void Framework::ShowFeatureByMercator(m2::PointD const & pt) place_page::Info info; std::string name; - GetBookmarkManager().SelectionMark()->SetPtOrg(pt); + GetBookmarkManager().SelectionMark().SetPtOrg(pt); FillPointInfo(pt, name, info); ActivateMapSelection(false, df::SelectionShape::OBJECT_POI, info); m_lastTapEvent = MakeTapEvent(info.GetMercator(), info.GetID(), TapEvent::Source::Other); @@ -1519,7 +1513,7 @@ void Framework::SelectSearchResult(search::Result const & result, bool animation if (m_drapeEngine != nullptr) m_drapeEngine->SetModelViewCenter(center, scale, animation, true /* trackVisibleViewport */); - GetBookmarkManager().SelectionMark()->SetPtOrg(center); + GetBookmarkManager().SelectionMark().SetPtOrg(center); ActivateMapSelection(false, df::SelectionShape::OBJECT_POI, info); m_lastTapEvent = MakeTapEvent(center, info.GetID(), TapEvent::Source::Search); } @@ -1620,8 +1614,7 @@ void Framework::FillSearchResultsMarks(bool clear, search::Results::ConstIter be if (!r.HasPoint()) continue; - auto mark = static_cast(bmManager.CreateUserMark(UserMark::Type::SEARCH, r.GetFeatureCenter())); - ASSERT_EQUAL(mark->GetMarkType(), UserMark::Type::SEARCH, ()); + auto * mark = bmManager.CreateUserMark(r.GetFeatureCenter()); auto const isFeature = r.GetResultType() == search::Result::Type::Feature; if (isFeature) mark->SetFoundFeature(r.GetFeatureID()); @@ -2017,7 +2010,7 @@ bool Framework::ShowMapForURL(string const & url) } else { - GetBookmarkManager().SelectionMark()->SetPtOrg(point); + GetBookmarkManager().SelectionMark().SetPtOrg(point); FillPointInfo(point, name, info); ActivateMapSelection(false, df::SelectionShape::OBJECT_POI, info); } @@ -2114,29 +2107,6 @@ BookmarkManager const & Framework::GetBookmarkManager() const return *m_bmManager.get(); } -BookmarkAndCategory Framework::FindBookmark(UserMark const * mark) const -{ - Bookmark const * bookmark = static_cast(mark); - BookmarkAndCategory empty; - BookmarkAndCategory result; - ASSERT_LESS_OR_EQUAL(GetBookmarkManager().GetBmCategoriesIds().size(), numeric_limits::max(), ()); - result.m_categoryIndex = bookmark->GetCategoryId(); - ASSERT(result.m_categoryIndex != empty.m_categoryIndex, ()); - size_t const sz = GetBookmarkManager().GetUserMarkCount(result.m_categoryIndex); - ASSERT_LESS_OR_EQUAL(sz, numeric_limits::max(), ()); - for (size_t i = 0; i < sz; ++i) - { - if (bookmark == GetBookmarkManager().GetBookmark(result.m_categoryIndex, i)) - { - result.m_bookmarkIndex = static_cast(i); - break; - } - } - - ASSERT(result.IsValid(), ()); - return result; -} - void Framework::SetMapSelectionListeners(TActivateMapSelectionFn const & activator, TDeactivateMapSelectionFn const & deactivator) { @@ -2313,7 +2283,7 @@ FeatureID Framework::FindBuildingAtPoint(m2::PointD const & mercator) const } df::SelectionShape::ESelectedObject Framework::OnTapEventImpl(TapEvent const & tapEvent, - place_page::Info & outInfo) const + place_page::Info & outInfo) { if (m_drapeEngine == nullptr) return df::SelectionShape::OBJECT_EMPTY; @@ -2337,7 +2307,7 @@ df::SelectionShape::ESelectedObject Framework::OnTapEventImpl(TapEvent const & t FillApiMarkInfo(*static_cast(mark), outInfo); break; case UserMark::Type::BOOKMARK: - FillBookmarkInfo(*static_cast(mark), FindBookmark(mark), outInfo); + FillBookmarkInfo(*static_cast(mark), outInfo); break; case UserMark::Type::SEARCH: FillSearchResultInfo(*static_cast(mark), outInfo); @@ -2370,7 +2340,7 @@ df::SelectionShape::ESelectedObject Framework::OnTapEventImpl(TapEvent const & t if (showMapSelection) { - GetBookmarkManager().SelectionMark()->SetPtOrg(outInfo.GetMercator()); + GetBookmarkManager().SelectionMark().SetPtOrg(outInfo.GetMercator()); return df::SelectionShape::OBJECT_POI; } @@ -3220,9 +3190,9 @@ void Framework::ClearViewportSearchResults() boost::optional Framework::GetCurrentPosition() const { auto const & myPosMark = GetBookmarkManager().MyPositionMark(); - if (!myPosMark->HasPosition()) + if (!myPosMark.HasPosition()) return {}; - return myPosMark->GetPivot(); + return myPosMark.GetPivot(); } bool Framework::ParseSearchQueryCommand(search::SearchParams const & params) diff --git a/map/framework.hpp b/map/framework.hpp index 5daf0a01de..c37010bc2d 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -315,13 +315,12 @@ public: /// Scans and loads all kml files with bookmarks in WritableDir. void LoadBookmarks(); - /// @return Created bookmark index in category. - size_t AddBookmark(size_t categoryIndex, m2::PointD const & ptOrg, BookmarkData & bm); - /// @return New moved bookmark index in category. - size_t MoveBookmark(size_t bmIndex, size_t curCatIndex, size_t newCatIndex); - void ReplaceBookmark(size_t catIndex, size_t bmIndex, BookmarkData const & bm); - /// @return Created bookmark category index. - size_t AddCategory(string const & categoryName); + /// @return Created bookmark id. + df::MarkID AddBookmark(df::MarkGroupID catId, m2::PointD const & ptOrg, BookmarkData & bm); + void MoveBookmark(df::MarkID bmId, df::MarkGroupID curCatId, df::MarkGroupID newCatId); + void ReplaceBookmark(df::MarkID bmId, BookmarkData const & bm); + /// @return Created bookmark category id. + df::MarkGroupID AddCategory(string const & categoryName); size_t LastEditedBMCategory() { return GetBookmarkManager().LastEditedBMCategory(); } string LastEditedBMType() const { return GetBookmarkManager().LastEditedBMType(); } @@ -331,7 +330,7 @@ public: bool DeleteBmCategory(size_t index); void ShowBookmark(df::MarkID id); - void ShowBookmark(BookmarkAndCategory const & bnc); + void ShowBookmark(Bookmark const * bookmark); void ShowTrack(Track const & track); void ShowFeatureByMercator(m2::PointD const & pt); @@ -339,7 +338,6 @@ public: void AddBookmarksFile(string const & filePath, bool isTemporaryFile); - BookmarkAndCategory FindBookmark(UserMark const * mark) const; BookmarkManager & GetBookmarkManager(); BookmarkManager const & GetBookmarkManager() const; @@ -365,7 +363,6 @@ private: df::SelectionShape::ESelectedObject selectionType, place_page::Info const & info); void InvalidateUserMarks(); - void ShowBookmark(Bookmark const * bookmark, BookmarkAndCategory const & bnc); public: void DeactivateMapSelection(bool notifyUI); @@ -423,7 +420,7 @@ private: void OnTapEvent(TapEvent const & tapEvent); /// outInfo is valid only if return value is not df::SelectionShape::OBJECT_EMPTY. df::SelectionShape::ESelectedObject OnTapEventImpl(TapEvent const & tapEvent, - place_page::Info & outInfo) const; + place_page::Info & outInfo); unique_ptr MakeTapEvent(m2::PointD const & center, FeatureID const & fid, TapEvent::Source source) const; UserMark const * FindUserMarkInTapPosition(df::TapInfo const & tapInfo) const; @@ -661,7 +658,7 @@ private: void FillRouteMarkInfo(RouteMarkPoint const & rmp, place_page::Info & info) const; public: - void FillBookmarkInfo(Bookmark const & bmk, BookmarkAndCategory const & bac, place_page::Info & info) const; + void FillBookmarkInfo(Bookmark const & bmk, place_page::Info & info) const; void ResetBookmarkInfo(Bookmark const & bmk, place_page::Info & info) const; /// @returns address of nearby building with house number in approx 1km distance. diff --git a/map/local_ads_manager.cpp b/map/local_ads_manager.cpp index 551d796643..c5f3403dfe 100644 --- a/map/local_ads_manager.cpp +++ b/map/local_ads_manager.cpp @@ -146,9 +146,7 @@ void CreateLocalAdsMarks(BookmarkManager * bmManager, CampaignData const & campa { for (auto const & data : campaignData) { - auto userMark = bmManager->CreateUserMark(UserMark::Type::LOCAL_ADS, data.second.m_position); - ASSERT(dynamic_cast(userMark) != nullptr, ()); - LocalAdsMark * mark = static_cast(userMark); + auto * mark = bmManager->CreateUserMark(data.second.m_position); mark->SetData(LocalAdsMarkData(data.second)); mark->SetFeatureId(data.first); } @@ -163,16 +161,11 @@ void DeleteLocalAdsMarks(BookmarkManager * bmManager, MwmSet::MwmId const & mwmI GetPlatform().RunTask(Platform::Thread::Gui, [bmManager, mwmId]() { - for (size_t i = 0; i < bmManager->GetUserMarkCount(UserMark::Type::LOCAL_ADS);) - { - auto userMark = bmManager->GetUserMark(UserMark::Type::LOCAL_ADS, i); - ASSERT(dynamic_cast(userMark) != nullptr, ()); - LocalAdsMark const * mark = static_cast(userMark); - if (mark->GetFeatureID().m_mwmId == mwmId) - bmManager->DeleteUserMark(UserMark::Type::LOCAL_ADS, i); - else - ++i; - } + bmManager->DeleteUserMarks(UserMark::Type::LOCAL_ADS, + [&mwmId](LocalAdsMark const * mark) + { + return mark->GetFeatureID().m_mwmId == mwmId; + }); bmManager->NotifyChanges(UserMark::Type::LOCAL_ADS); }); } diff --git a/map/mwm_url.cpp b/map/mwm_url.cpp index c6478a6ea4..40d64cba1b 100644 --- a/map/mwm_url.cpp +++ b/map/mwm_url.cpp @@ -196,7 +196,7 @@ ParsedMapApi::ParsingResult ParsedMapApi::Parse(Uri const & uri) for (auto const & p : points) { m2::PointD glPoint(MercatorBounds::FromLatLon(p.m_lat, p.m_lon)); - ApiMarkPoint * mark = static_cast(m_bmManager->CreateUserMark(UserMark::Type::API, glPoint)); + auto * mark = m_bmManager->CreateUserMark(glPoint); mark->SetName(p.m_name); mark->SetApiID(p.m_id); mark->SetStyle(style::GetSupportedStyle(p.m_style, p.m_name, "")); @@ -446,18 +446,18 @@ void ParsedMapApi::Reset() bool ParsedMapApi::GetViewportRect(m2::RectD & rect) const { ASSERT(m_bmManager != nullptr, ()); - size_t const markCount = m_bmManager->GetUserMarkCount(UserMark::Type::API); - if (markCount == 1 && m_zoomLevel >= 1) + auto const & markIds = m_bmManager->GetUserMarkIds(UserMark::Type::API); + if (markIds.size() == 1 && m_zoomLevel >= 1) { double zoom = min(static_cast(scales::GetUpperComfortScale()), m_zoomLevel); - rect = df::GetRectForDrawScale(zoom, m_bmManager->GetUserMark(UserMark::Type::API, 0)->GetPivot()); + rect = df::GetRectForDrawScale(zoom, m_bmManager->GetUserMark(*markIds.begin())->GetPivot()); return true; } else { m2::RectD result; - for (size_t i = 0; i < markCount; ++i) - result.Add(m_bmManager->GetUserMark(UserMark::Type::API, i)->GetPivot()); + for (auto markId : markIds) + result.Add(m_bmManager->GetUserMark(markId)->GetPivot()); if (result.IsValid()) { @@ -472,10 +472,11 @@ bool ParsedMapApi::GetViewportRect(m2::RectD & rect) const ApiMarkPoint const * ParsedMapApi::GetSinglePoint() const { ASSERT(m_bmManager != nullptr, ()); - if (m_bmManager->GetUserMarkCount(UserMark::Type::API) != 1) + auto const & markIds = m_bmManager->GetUserMarkIds(UserMark::Type::API); + if (markIds.size() != 1) return nullptr; - return static_cast(m_bmManager->GetUserMark(UserMark::Type::API, 0)); + return static_cast(m_bmManager->GetUserMark(*markIds.begin())); } } diff --git a/map/place_page_info.cpp b/map/place_page_info.cpp index 3ae7f674b1..2d5552b8f1 100644 --- a/map/place_page_info.cpp +++ b/map/place_page_info.cpp @@ -186,9 +186,9 @@ void Info::SetCustomNameWithCoordinates(m2::PointD const & mercator, std::string m_customName = name; } -void Info::SetBac(BookmarkAndCategory const & bac) +void Info::SetBookmarkId(df::MarkID markId) { - m_bac = bac; + m_markId = markId; m_uiSubtitle = FormatSubtitle(IsFeature() /* withType */); } diff --git a/map/place_page_info.hpp b/map/place_page_info.hpp index be0fead870..f79ef41dbd 100644 --- a/map/place_page_info.hpp +++ b/map/place_page_info.hpp @@ -69,7 +69,7 @@ public: /// Place traits bool IsFeature() const { return m_featureID.IsValid(); } - bool IsBookmark() const { return m_bac.IsValid(); } + bool IsBookmark() const { return m_markGroupId != 0; } bool IsMyPosition() const { return m_isMyPosition; } bool IsRoutePoint() const { return m_isRoutePoint; } @@ -116,9 +116,11 @@ public: void SetLocalizedWifiString(std::string const & str) { m_localizedWifiString = str; } /// Bookmark - BookmarkAndCategory const & GetBookmarkAndCategory() const { return m_bac; } + void SetBookmarkId(df::MarkID markId); + df::MarkID GetBookmarkId() const { return m_markId; } + void SetBookmarkCategoryId(df::MarkGroupID markGroupId) { m_markGroupId = markGroupId; } + df::MarkGroupID GetBookmarkCategoryId() const { return m_markGroupId; } std::string const & GetBookmarkCategoryName() const { return m_bookmarkCategoryName; } - void SetBac(BookmarkAndCategory const & bac); void SetBookmarkCategoryName(std::string const & name) { m_bookmarkCategoryName = name; } void SetBookmarkData(BookmarkData const & data) { m_bookmarkData = data; } BookmarkData const & GetBookmarkData() const { return m_bookmarkData; } @@ -235,7 +237,8 @@ private: /// Bookmarks /// If not empty, bookmark is bound to this place page. - BookmarkAndCategory m_bac; + df::MarkID m_markId = 0; + df::MarkGroupID m_markGroupId = 0; /// Bookmark category name. Empty, if it's not bookmark; std::string m_bookmarkCategoryName; BookmarkData m_bookmarkData; diff --git a/map/routing_manager.cpp b/map/routing_manager.cpp index 597e8462c6..90774d1a6a 100644 --- a/map/routing_manager.cpp +++ b/map/routing_manager.cpp @@ -110,7 +110,7 @@ RouteMarkData GetLastPassedPoint(BookmarkManager * bmManager, vectorMyPositionMark()->GetPivot(); + data.m_position = bmManager->MyPositionMark().GetPivot(); data.m_isMyPosition = false; } @@ -247,10 +247,9 @@ RoutingManager::RoutingManager(Callbacks && callbacks, Delegate & delegate) #ifdef SHOW_ROUTE_DEBUG_MARKS if (m_bmManager == nullptr) return; - auto & controller = m_bmManager->GetUserMarksController(UserMark::Type::DEBUG_MARK); - controller.SetIsVisible(true); - controller.CreateUserMark(pt); - controller.NotifyChanges(); + m_bmManager->SetIsVisible(UserMark::Type::DEBUG_MARK, true); + m_bmManager->CreateUserMark(pt); + m_bmManager->NotifyChanges(UserMark::Type::DEBUG_MARK); #endif }); @@ -629,7 +628,8 @@ bool RoutingManager::CouldAddIntermediatePoint() const if (!IsRoutingActive()) return false; - return m_bmManager->GetUserMarkCount(UserMark::Type::ROUTING) < RoutePointsLayout::kMaxIntermediatePointsCount + 2; + return m_bmManager->GetUserMarkIds(UserMark::Type::ROUTING).size() + < RoutePointsLayout::kMaxIntermediatePointsCount + 2; } void RoutingManager::AddRoutePoint(RouteMarkData && markData) @@ -802,12 +802,12 @@ void RoutingManager::BuildRoute(uint32_t timeoutSec) continue; auto const & myPosition = m_bmManager->MyPositionMark(); - if (!myPosition->HasPosition()) + if (!myPosition.HasPosition()) { CallRouteBuilded(IRouter::NoCurrentPosition, storage::TCountriesVec()); return; } - p.m_position = myPosition->GetPivot(); + p.m_position = myPosition.GetPivot(); } // Check for equal points. @@ -1172,12 +1172,12 @@ bool RoutingManager::LoadRoutePoints() m_bmManager->ClearUserMarks(UserMark::Type::ROUTING); for (auto & p : points) { - if (p.m_pointType == RouteMarkType::Start && myPosMark->HasPosition()) + if (p.m_pointType == RouteMarkType::Start && myPosMark.HasPosition()) { RouteMarkData startPt; startPt.m_pointType = RouteMarkType::Start; startPt.m_isMyPosition = true; - startPt.m_position = myPosMark->GetPivot(); + startPt.m_position = myPosMark.GetPivot(); AddRoutePoint(move(startPt)); } else @@ -1188,7 +1188,7 @@ bool RoutingManager::LoadRoutePoints() // If we don't have my position, save loading timestamp. Probably // we will get my position soon. - if (!myPosMark->HasPosition()) + if (!myPosMark.HasPosition()) m_loadRoutePointsTimestamp = chrono::steady_clock::now(); return true; diff --git a/map/routing_mark.cpp b/map/routing_mark.cpp index 2ff28bde41..19d3278935 100644 --- a/map/routing_mark.cpp +++ b/map/routing_mark.cpp @@ -178,7 +178,7 @@ RoutePointsLayout::RoutePointsLayout(BookmarkManager & manager) RouteMarkPoint * RoutePointsLayout::AddRoutePoint(RouteMarkData && data) { - auto const count = m_manager.GetUserMarkCount(UserMark::Type::ROUTING); + auto const count = m_manager.GetUserMarkIds(UserMark::Type::ROUTING).size(); if (count == kMaxIntermediatePointsCount + 2) return nullptr; @@ -216,9 +216,7 @@ RouteMarkPoint * RoutePointsLayout::AddRoutePoint(RouteMarkData && data) } } } - auto userMark = m_manager.CreateUserMark(UserMark::Type::ROUTING, data.m_position); - ASSERT(dynamic_cast(userMark) != nullptr, ()); - RouteMarkPoint * newPoint = static_cast(userMark); + auto * newPoint = m_manager.CreateUserMark(data.m_position); newPoint->SetMarkData(std::move(data)); return newPoint; @@ -226,11 +224,10 @@ RouteMarkPoint * RoutePointsLayout::AddRoutePoint(RouteMarkData && data) bool RoutePointsLayout::RemoveRoutePoint(RouteMarkType type, size_t intermediateIndex) { - RouteMarkPoint * point = nullptr; - size_t index = 0; - for (size_t sz = m_manager.GetUserMarkCount(UserMark::Type::ROUTING); index < sz; ++index) + RouteMarkPoint const * point = nullptr; + for (auto markId : m_manager.GetUserMarkIds(UserMark::Type::ROUTING)) { - RouteMarkPoint * mark = GetRouteMarkForEdit(index); + auto const * mark = m_manager.GetMark(markId); if (mark->IsEqualFullType(type, intermediateIndex)) { point = mark; @@ -276,7 +273,7 @@ bool RoutePointsLayout::RemoveRoutePoint(RouteMarkType type, size_t intermediate }); } - m_manager.DeleteUserMark(UserMark::Type::ROUTING, index); + m_manager.DeleteUserMark(point->GetId()); return true; } @@ -287,17 +284,11 @@ void RoutePointsLayout::RemoveRoutePoints() void RoutePointsLayout::RemoveIntermediateRoutePoints() { - for (size_t i = 0, sz = m_manager.GetUserMarkCount(UserMark::Type::ROUTING); i < sz;) - { - RouteMarkPoint const * mark = GetRouteMark(i); - if (mark->GetRoutePointType() == RouteMarkType::Intermediate) - { - m_manager.DeleteUserMark(UserMark::Type::ROUTING, i); - --sz; - } - else - ++i; - } + m_manager.DeleteUserMarks(UserMark::Type::ROUTING, + [](RouteMarkPoint const * mark) + { + return mark->GetRoutePointType() == RouteMarkType::Intermediate; + }); } bool RoutePointsLayout::MoveRoutePoint(RouteMarkType currentType, size_t currentIntermediateIndex, @@ -328,42 +319,42 @@ void RoutePointsLayout::PassRoutePoint(RouteMarkType type, size_t intermediateIn void RoutePointsLayout::SetFollowingMode(bool enabled) { - for (size_t i = 0, sz = m_manager.GetUserMarkCount(UserMark::Type::ROUTING); i < sz; ++i) - GetRouteMarkForEdit(i)->SetFollowingMode(enabled); + for (auto markId : m_manager.GetUserMarkIds(UserMark::Type::ROUTING)) + m_manager.GetMarkForEdit(markId)->SetFollowingMode(enabled); } RouteMarkPoint * RoutePointsLayout::GetRoutePoint(RouteMarkType type, size_t intermediateIndex) { - for (size_t i = 0, sz = m_manager.GetUserMarkCount(UserMark::Type::ROUTING); i < sz; ++i) + for (auto markId : m_manager.GetUserMarkIds(UserMark::Type::ROUTING)) { - RouteMarkPoint * mark = GetRouteMarkForEdit(i); + auto const * mark = m_manager.GetMark(markId); if (mark->IsEqualFullType(type, intermediateIndex)) - return mark; + return m_manager.GetMarkForEdit(markId); } return nullptr; } RouteMarkPoint * RoutePointsLayout::GetMyPositionPoint() { - for (size_t i = 0, sz = m_manager.GetUserMarkCount(UserMark::Type::ROUTING); i < sz; ++i) + for (auto markId : m_manager.GetUserMarkIds(UserMark::Type::ROUTING)) { - RouteMarkPoint * mark = GetRouteMarkForEdit(i); + auto const * mark = m_manager.GetMark(markId); if (mark->IsMyPosition()) - return mark; + return m_manager.GetMarkForEdit(markId); } return nullptr; } std::vector RoutePointsLayout::GetRoutePoints() { - size_t const sz = m_manager.GetUserMarkCount(UserMark::Type::ROUTING); + auto const & markIds = m_manager.GetUserMarkIds(UserMark::Type::ROUTING); std::vector points; - points.reserve(sz); + points.reserve(markIds.size()); RouteMarkPoint * startPoint = nullptr; RouteMarkPoint * finishPoint = nullptr; - for (size_t i = 0; i < sz; ++i) + for (auto markId : markIds) { - RouteMarkPoint * p = GetRouteMarkForEdit(i); + auto * p = m_manager.GetMarkForEdit(markId); if (p->GetRoutePointType() == RouteMarkType::Start) startPoint = p; else if (p->GetRoutePointType() == RouteMarkType::Finish) @@ -384,28 +375,14 @@ std::vector RoutePointsLayout::GetRoutePoints() size_t RoutePointsLayout::GetRoutePointsCount() const { - return m_manager.GetUserMarkCount(UserMark::Type::ROUTING); -} - -RouteMarkPoint * RoutePointsLayout::GetRouteMarkForEdit(size_t index) -{ - auto userMark = m_manager.GetUserMarkForEdit(UserMark::Type::ROUTING, index); - ASSERT(dynamic_cast(userMark) != nullptr, ()); - return static_cast(userMark); -} - -RouteMarkPoint const * RoutePointsLayout::GetRouteMark(size_t index) -{ - auto userMark = m_manager.GetUserMark(UserMark::Type::ROUTING, index); - ASSERT(dynamic_cast(userMark) != nullptr, ()); - return static_cast(userMark); + return m_manager.GetUserMarkIds(UserMark::Type::ROUTING).size(); } void RoutePointsLayout::ForEachIntermediatePoint(TRoutePointCallback const & fn) { - for (size_t i = 0, sz = m_manager.GetUserMarkCount(UserMark::Type::ROUTING); i < sz; ++i) + for (auto markId : m_manager.GetUserMarkIds(UserMark::Type::ROUTING)) { - RouteMarkPoint * mark = GetRouteMarkForEdit(i); + auto * mark = m_manager.GetMarkForEdit(markId); if (mark->GetRoutePointType() == RouteMarkType::Intermediate) fn(mark); } diff --git a/map/routing_mark.hpp b/map/routing_mark.hpp index 39cae19dfe..cbeba826f6 100644 --- a/map/routing_mark.hpp +++ b/map/routing_mark.hpp @@ -97,8 +97,6 @@ public: private: using TRoutePointCallback = function; void ForEachIntermediatePoint(TRoutePointCallback const & fn); - RouteMarkPoint * GetRouteMarkForEdit(size_t index); - RouteMarkPoint const * GetRouteMark(size_t index); BookmarkManager & m_manager; }; diff --git a/map/search_mark.cpp b/map/search_mark.cpp index 9a8065b6e5..a2b6f72b7b 100644 --- a/map/search_mark.cpp +++ b/map/search_mark.cpp @@ -126,10 +126,9 @@ void SearchMarks::SetPreparingState(std::vector const & features, boo ASSERT(std::is_sorted(features.begin(), features.end()), ()); - size_t const count = m_bmManager->GetUserMarkCount(UserMark::Type::SEARCH); - for (size_t i = 0; i < count; ++i) + for (auto markId : m_bmManager->GetUserMarkIds(UserMark::Type::SEARCH)) { - auto mark = static_cast(m_bmManager->GetUserMarkForEdit(UserMark::Type::SEARCH, i)); + auto mark = static_cast(m_bmManager->GetUserMarkForEdit(markId)); if (std::binary_search(features.begin(), features.end(), mark->GetFeatureID())) mark->SetPreparing(isPreparing); } diff --git a/map/track.cpp b/map/track.cpp index 1bde42fbc9..90ea17e342 100644 --- a/map/track.cpp +++ b/map/track.cpp @@ -9,6 +9,7 @@ Track::Track(Track::PolylineD const & polyline, Track::Params const & p) : m_polyline(polyline) , m_params(p) + , m_groupID(0) { ASSERT_GREATER(m_polyline.GetSize(), 1, ()); } @@ -71,3 +72,14 @@ std::vector const & Track::GetPoints() const { return m_polyline.GetPoints(); } + +void Track::Attach(df::MarkGroupID groupID) +{ + ASSERT(!m_groupID, ()); + m_groupID = groupID; +} + +void Track::Detach() +{ + m_groupID = 0; +} diff --git a/map/track.hpp b/map/track.hpp index 372c8bf54e..cc868c9890 100644 --- a/map/track.hpp +++ b/map/track.hpp @@ -50,8 +50,13 @@ public: float GetDepth(size_t layerIndex) const override; std::vector const & GetPoints() const override; + df::MarkGroupID GetGroupId() const { return m_groupID; } + void Attach(df::MarkGroupID groupID); + void Detach(); + private: PolylineD m_polyline; Params m_params; + df::MarkGroupID m_groupID; mutable bool m_isDirty = true; }; diff --git a/map/transit/transit_display.cpp b/map/transit/transit_display.cpp index 984006d0be..5ea26e1b9c 100644 --- a/map/transit/transit_display.cpp +++ b/map/transit/transit_display.cpp @@ -122,7 +122,6 @@ void AddTransitGateSegment(m2::PointD const & destPoint, df::ColorConstant const ASSERT_GREATER(subroute.m_polyline.GetSize(), 0, ()); df::SubrouteStyle style(color, df::RoutePattern(4.0, 2.0)); style.m_startIndex = subroute.m_polyline.GetSize() - 1; - auto const vec = destPoint - subroute.m_polyline.Back(); subroute.m_polyline.Add(destPoint); style.m_endIndex = subroute.m_polyline.GetSize() - 1; subroute.AddStyle(style); @@ -459,12 +458,9 @@ void TransitRouteDisplay::CollectTransitDisplayInfo(vector const & TransitMark * TransitRouteDisplay::CreateMark(m2::PointD const & pt, FeatureID const & fid) { - uint32_t const nextIndex = static_cast(m_bmManager->GetUserMarkCount(UserMark::Type::TRANSIT)); - - auto userMark = m_bmManager->CreateUserMark(UserMark::Type::TRANSIT, pt); - ASSERT(dynamic_cast(userMark) != nullptr, ()); - auto transitMark = static_cast(userMark); + uint32_t const nextIndex = static_cast(m_bmManager->GetUserMarkIds(UserMark::Type::TRANSIT).size()); + auto * transitMark = m_bmManager->CreateUserMark(pt); transitMark->SetFeatureId(fid); transitMark->SetIndex(nextIndex); return transitMark; diff --git a/map/user_mark.cpp b/map/user_mark.cpp index 5e97dd1c64..a32603fee2 100644 --- a/map/user_mark.cpp +++ b/map/user_mark.cpp @@ -36,8 +36,8 @@ ms::LatLon UserMark::GetLatLon() const return MercatorBounds::ToLatLon(m_ptOrg); } -StaticMarkPoint::StaticMarkPoint() - : UserMark(m2::PointD{}, UserMark::Type::STATIC) +StaticMarkPoint::StaticMarkPoint(m2::PointD const & ptOrg) + : UserMark(ptOrg, UserMark::Type::STATIC) {} void StaticMarkPoint::SetPtOrg(m2::PointD const & ptOrg) @@ -46,8 +46,8 @@ void StaticMarkPoint::SetPtOrg(m2::PointD const & ptOrg) m_ptOrg = ptOrg; } -MyPositionMarkPoint::MyPositionMarkPoint() - : StaticMarkPoint() +MyPositionMarkPoint::MyPositionMarkPoint(m2::PointD const & ptOrg) + : StaticMarkPoint(ptOrg) {} DebugMarkPoint::DebugMarkPoint(const m2::PointD & ptOrg) diff --git a/map/user_mark.hpp b/map/user_mark.hpp index 0e8ffb782e..6dad85cc51 100644 --- a/map/user_mark.hpp +++ b/map/user_mark.hpp @@ -39,8 +39,7 @@ public: TRANSIT, LOCAL_ADS, DEBUG_MARK, - BOOKMARK, - PREDEFINED_COUNT = BOOKMARK + BOOKMARK, // Should always be the last one }; UserMark(m2::PointD const & ptOrg, UserMark::Type type); @@ -52,7 +51,7 @@ public: m2::PointD const & GetPivot() const override; m2::PointD GetPixelOffset() const override; dp::Anchor GetAnchor() const override; - virtual float GetDepth() const override { return 0.0f; }; + virtual float GetDepth() const override { return 0.0f; } df::RenderState::DepthLayer GetDepthLayer() const override; drape_ptr GetTitleDecl() const override { return nullptr; } drape_ptr GetColoredSymbols() const override { return nullptr; } @@ -69,6 +68,8 @@ public: ms::LatLon GetLatLon() const; virtual Type GetMarkType() const { return m_type; } + virtual df::MarkGroupID GetGroupId() const { return m_type; } + virtual bool IsAvailableForSearch() const { return true; } protected: @@ -86,7 +87,7 @@ private: class StaticMarkPoint : public UserMark { public: - explicit StaticMarkPoint(); + explicit StaticMarkPoint(m2::PointD const & ptOrg); drape_ptr GetSymbolNames() const override { return nullptr; } @@ -96,7 +97,7 @@ public: class MyPositionMarkPoint : public StaticMarkPoint { public: - explicit MyPositionMarkPoint(); + explicit MyPositionMarkPoint(m2::PointD const & ptOrg); void SetUserPosition(m2::PointD const & pt, bool hasPosition) { diff --git a/map/user_mark_container.cpp b/map/user_mark_container.cpp index 48746b9fce..ed91712c7b 100644 --- a/map/user_mark_container.cpp +++ b/map/user_mark_container.cpp @@ -11,105 +11,16 @@ #include #include -namespace -{ -class FindMarkFunctor -{ -public: - FindMarkFunctor(UserMark ** mark, double & minD, m2::AnyRectD const & rect) - : m_mark(mark) - , m_minD(minD) - , m_rect(rect) - { - m_globalCenter = rect.GlobalCenter(); - } - - void operator()(UserMark * mark) - { - m2::PointD const & org = mark->GetPivot(); - if (m_rect.IsPointInside(org)) - { - double minDCandidate = m_globalCenter.SquareLength(org); - if (minDCandidate < m_minD) - { - *m_mark = mark; - m_minD = minDCandidate; - } - } - } - - UserMark ** m_mark; - double & m_minD; - m2::AnyRectD const & m_rect; - m2::PointD m_globalCenter; -}; - -size_t const VisibleFlag = 0; -size_t const DrawableFlag = 1; - -df::MarkGroupID GenerateMarkGroupId(UserMarkContainer const * cont) -{ - return reinterpret_cast(cont); -} -} // namespace - UserMarkContainer::UserMarkContainer(UserMark::Type type, Listeners const & listeners) : m_type(type) , m_listeners(listeners) { - m_flags.set(); } UserMarkContainer::~UserMarkContainer() { Clear(); - NotifyChanges(); -} - -void UserMarkContainer::SetDrapeEngine(ref_ptr engine) -{ - m_drapeEngine.Set(engine); -} - -UserMark const * UserMarkContainer::GetUserMarkById(df::MarkID id) const -{ - auto const it = m_userMarksDict.find(id); - if (it != m_userMarksDict.end()) - return it->second; - return nullptr; -} - -UserMark const * UserMarkContainer::GetUserMarkById(df::MarkID id, size_t & index) const -{ - auto const it = m_userMarksDict.find(id); - if (it != m_userMarksDict.end()) - { - for (size_t i = 0; i < m_userMarks.size(); ++i) - { - if (m_userMarks[i]->GetId() == id) - { - index = i; - return m_userMarks[i].get(); - } - } - } - return nullptr; -} - -UserMark const * UserMarkContainer::FindMarkInRect(m2::AnyRectD const & rect, double & d) const -{ - UserMark * mark = nullptr; - if (IsVisible()) - { - FindMarkFunctor f(&mark, d, rect); - for (size_t i = 0; i < m_userMarks.size(); ++i) - { - if (m_userMarks[i]->IsAvailableForSearch() && rect.IsPointInside(m_userMarks[i]->GetPivot())) - f(m_userMarks[i].get()); - } - } - return mark; } void UserMarkContainer::NotifyListeners() @@ -122,16 +33,10 @@ void UserMarkContainer::NotifyListeners() df::IDCollection marks(m_createdMarks.begin(), m_createdMarks.end()); m_listeners.m_createListener(*this, marks); } - if (m_listeners.m_updateListener != nullptr) + if (m_listeners.m_updateListener != nullptr && !m_updatedMarks.empty()) { - 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); + df::IDCollection marks(m_updatedMarks.begin(), m_updatedMarks.end()); + m_listeners.m_updateListener(*this, marks); } if (m_listeners.m_deleteListener != nullptr && !m_removedMarks.empty()) { @@ -140,75 +45,19 @@ void UserMarkContainer::NotifyListeners() } } -void UserMarkContainer::NotifyChanges() -{ - if (!IsDirty()) - return; - - NotifyListeners(); - - df::DrapeEngineLockGuard lock(m_drapeEngine); - if (!lock) - return; - - auto engine = lock.Get(); - - df::MarkGroupID const groupId = GenerateMarkGroupId(this); - engine->ChangeVisibilityUserMarksGroup(groupId, IsVisible() && IsDrawable()); - - if (GetUserPointCount() == 0 && GetUserLineCount() == 0) - { - engine->UpdateUserMarksGroup(groupId, this); - engine->ClearUserMarksGroup(groupId); - } - else if (IsVisible() && IsDrawable()) - { - engine->UpdateUserMarksGroup(groupId, this); - } - - engine->InvalidateUserMarks(); -} - size_t UserMarkContainer::GetUserPointCount() const { return m_userMarks.size(); } -df::UserPointMark const * UserMarkContainer::GetUserPointMark(size_t index) const -{ - return GetUserMark(index); -} - size_t UserMarkContainer::GetUserLineCount() const { - return 0; -} - -df::UserLineMark const * UserMarkContainer::GetUserLineMark(size_t index) const -{ - UNUSED_VALUE(index); - ASSERT(false, ()); - return nullptr; + return m_userLines.size(); } bool UserMarkContainer::IsVisible() const { - return m_flags[VisibleFlag]; -} - -bool UserMarkContainer::IsDrawable() const -{ - return m_flags[DrawableFlag]; -} - -UserMark * UserMarkContainer::CreateUserMark(m2::PointD const & ptOrg) -{ - // Push front an user mark. - SetDirty(); - m_userMarks.push_front(unique_ptr(AllocateUserMark(ptOrg))); - m_userMarksDict.insert(make_pair(m_userMarks.front()->GetId(), m_userMarks.front().get())); - m_createdMarks.insert(m_userMarks.front()->GetId()); - return m_userMarks.front().get(); + return m_isVisible; } size_t UserMarkContainer::GetUserMarkCount() const @@ -216,32 +65,18 @@ size_t UserMarkContainer::GetUserMarkCount() const return GetUserPointCount(); } -UserMark const * UserMarkContainer::GetUserMark(size_t index) const -{ - ASSERT_LESS(index, m_userMarks.size(), ()); - return m_userMarks[index].get(); -} - UserMark::Type UserMarkContainer::GetType() const { return m_type; } -UserMark * UserMarkContainer::GetUserMarkForEdit(size_t index) -{ - SetDirty(); - ASSERT_LESS(index, m_userMarks.size(), ()); - return m_userMarks[index].get(); -} - void UserMarkContainer::Clear() { SetDirty(); - - for (auto const & mark : m_userMarks) + for (auto const & markId : m_userMarks) { - if (m_createdMarks.find(mark->GetId()) == m_createdMarks.end()) - m_removedMarks.insert(mark->GetId()); + if (m_createdMarks.find(markId) == m_createdMarks.end()) + m_removedMarks.insert(markId); } m_createdMarks.clear(); m_userMarks.clear(); @@ -252,8 +87,7 @@ void UserMarkContainer::SetIsVisible(bool isVisible) if (IsVisible() != isVisible) { SetDirty(); - m_flags[VisibleFlag] = isVisible; - m_flags[DrawableFlag] = isVisible; + m_isVisible = isVisible; } } @@ -272,33 +106,18 @@ bool UserMarkContainer::IsDirty() const return m_isDirty; } -void UserMarkContainer::DeleteUserMark(size_t index) -{ - SetDirty(); - ASSERT_LESS(index, m_userMarks.size(), ()); - if (index < m_userMarks.size()) - { - auto const markId = m_userMarks[index]->GetId(); - auto const it = m_createdMarks.find(markId); - if (it != m_createdMarks.end()) - m_createdMarks.erase(it); - else - m_removedMarks.insert(markId); - m_userMarks.erase(m_userMarks.begin() + index); - m_userMarksDict.erase(markId); - } - else - { - LOG(LWARNING, ("Trying to delete non-existing item at index", index)); - } -} - -void UserMarkContainer::AcceptChanges(df::MarkIDCollection & createdMarks, +void UserMarkContainer::AcceptChanges(df::MarkIDCollection & groupMarks, + df::MarkIDCollection & createdMarks, df::MarkIDCollection & removedMarks) { + groupMarks.Clear(); createdMarks.Clear(); removedMarks.Clear(); + groupMarks.m_marksID.reserve(m_userMarks.size()); + for(auto const & markId : m_userMarks) + groupMarks.m_marksID.push_back(markId); + createdMarks.m_marksID.reserve(m_createdMarks.size()); for (auto const & markId : m_createdMarks) createdMarks.m_marksID.push_back(markId); @@ -309,5 +128,32 @@ void UserMarkContainer::AcceptChanges(df::MarkIDCollection & createdMarks, removedMarks.m_marksID.push_back(markId); m_removedMarks.clear(); + m_updatedMarks.clear(); + m_isDirty = false; } + + +void UserMarkContainer::AttachUserMark(df::MarkID markId) +{ + SetDirty(); + m_createdMarks.insert(markId); + m_userMarks.insert(markId); +} + +void UserMarkContainer::EditUserMark(df::MarkID markId) +{ + SetDirty(); + m_updatedMarks.insert(markId); +} + +void UserMarkContainer::DetachUserMark(df::MarkID markId) +{ + SetDirty(); + auto const it = m_createdMarks.find(markId); + if (it != m_createdMarks.end()) + m_createdMarks.erase(it); + else + m_removedMarks.insert(markId); + m_userMarks.erase(markId); +} diff --git a/map/user_mark_container.hpp b/map/user_mark_container.hpp index 8fb4e561f0..585674e86a 100644 --- a/map/user_mark_container.hpp +++ b/map/user_mark_container.hpp @@ -3,7 +3,6 @@ #include "map/user_mark.hpp" #include "drape_frontend/drape_engine_safe_ptr.hpp" -#include "drape_frontend/user_marks_provider.hpp" #include "geometry/point2d.hpp" #include "geometry/rect2d.hpp" @@ -11,16 +10,14 @@ #include -#include -#include #include #include #include -class UserMarkContainer : public df::UserMarksProvider +class UserMarkContainer { public: - using TUserMarksList = std::deque>; + using MarkIDSet = std::set; using NotifyChangesFn = std::function; struct Listeners @@ -41,63 +38,44 @@ public: UserMarkContainer(UserMark::Type type, Listeners const & listeners = Listeners()); - ~UserMarkContainer() override; + virtual ~UserMarkContainer(); -protected: - friend class BookmarkManager; - friend class KMLParser; - - void SetDrapeEngine(ref_ptr engine); - UserMark const * GetUserMarkById(df::MarkID id) const; - UserMark const * GetUserMarkById(df::MarkID id, size_t & index) const; - - // If not found mark on rect result is nullptr. - // If mark is found in "d" return distance from rect center. - // In multiple select choose mark with min(d). - UserMark const * FindMarkInRect(m2::AnyRectD const & rect, double & d) const; - - // UserMarksProvider implementation. - size_t GetUserPointCount() const override; - df::UserPointMark const * GetUserPointMark(size_t index) const override; - - size_t GetUserLineCount() const override; - df::UserLineMark const * GetUserLineMark(size_t index) const override; - - bool IsDirty() const override; + size_t GetUserPointCount() const; + virtual size_t GetUserLineCount() const; + bool IsDirty() const; // Discard isDirty flag, return id collection of removed marks since previous method call. - void AcceptChanges(df::MarkIDCollection & createdMarks, - df::MarkIDCollection & removedMarks) override; + virtual void AcceptChanges(df::MarkIDCollection & groupMarks, + df::MarkIDCollection & createdMarks, + df::MarkIDCollection & removedMarks); + void NotifyListeners(); bool IsVisible() const; - bool IsDrawable() const; size_t GetUserMarkCount() const; - UserMark const * GetUserMark(size_t index) const; UserMark::Type GetType() const; - UserMark * CreateUserMark(m2::PointD const & ptOrg); - UserMark * GetUserMarkForEdit(size_t index); - void DeleteUserMark(size_t index); + + MarkIDSet const & GetUserMarks() const { return m_userMarks; } + + void AttachUserMark(df::MarkID markId); + void EditUserMark(df::MarkID markId); + void DetachUserMark(df::MarkID markId); + void Clear(); void SetIsVisible(bool isVisible); void Update(); - void NotifyChanges(); protected: void SetDirty(); - virtual UserMark * AllocateUserMark(m2::PointD const & ptOrg) = 0; - -private: - void NotifyListeners(); - - df::DrapeEngineSafePtr m_drapeEngine; - std::bitset<4> m_flags; - double m_layerDepth; - TUserMarksList m_userMarks; UserMark::Type m_type; - std::set m_createdMarks; - std::set m_removedMarks; - std::map m_userMarksDict; + + MarkIDSet m_userMarks; + MarkIDSet m_userLines; + + MarkIDSet m_createdMarks; + MarkIDSet m_removedMarks; + MarkIDSet m_updatedMarks; + bool m_isVisible = true; bool m_isDirty = false; Listeners m_listeners; @@ -105,17 +83,3 @@ private: DISALLOW_COPY_AND_MOVE(UserMarkContainer); }; -template -class SpecifiedUserMarkContainer : public UserMarkContainer -{ -public: - explicit SpecifiedUserMarkContainer() - : UserMarkContainer(UserMarkType) - {} - -protected: - UserMark * AllocateUserMark(m2::PointD const & ptOrg) override - { - return new MarkPointClassType(ptOrg); - } -};