From 7b389016087a0acccc37948eb1e4c663c576bb66 Mon Sep 17 00:00:00 2001 From: Viktor Govako Date: Fri, 10 Jun 2022 13:34:56 +0300 Subject: [PATCH 1/4] [base] Removed copy-paste in Timer. Signed-off-by: Viktor Govako --- base/base_tests/small_set_test.cpp | 14 +++--- base/timer.cpp | 34 +------------ base/timer.hpp | 49 ++++++++++--------- routing/index_graph.cpp | 2 +- .../routes_builder_tool/utils.cpp | 24 +++++---- search/search_params.hpp | 26 +++++----- .../test_search_request.cpp | 2 +- .../test_search_request.hpp | 10 ++-- testing/testingmain.cpp | 2 +- .../archival_reporter_tests.cpp | 13 ++--- 10 files changed, 77 insertions(+), 99 deletions(-) diff --git a/base/base_tests/small_set_test.cpp b/base/base_tests/small_set_test.cpp index f9f863991a..c67f2d4c1d 100644 --- a/base/base_tests/small_set_test.cpp +++ b/base/base_tests/small_set_test.cpp @@ -121,7 +121,7 @@ UNIT_TEST(SmallMap_Benchmark1) // 3. Run unordered_map. { - base::Timer timer; + base::HighResTimer timer; for (auto i : indices) sum1 += (uMap.find(i) != uMap.end() ? 1 : 0); t1 = timer.ElapsedMilliseconds(); @@ -129,7 +129,7 @@ UNIT_TEST(SmallMap_Benchmark1) // 4. Run SmallMap. { - base::Timer timer; + base::HighResTimer timer; for (auto i : indices) sum2 += (sMap.Find(i) ? 1 : 0); t2 = timer.ElapsedMilliseconds(); @@ -193,7 +193,7 @@ UNIT_TEST(SmallMap_Benchmark2) // 3. Run unordered_map. { - base::Timer timer; + base::HighResTimer timer; for (auto i : indices) { auto const it = uMap.find(keys[i]); @@ -205,7 +205,7 @@ UNIT_TEST(SmallMap_Benchmark2) // 4. Run SmallMap. { - base::Timer timer; + base::HighResTimer timer; for (auto i : indices) { auto const * p = sMap.Find(keys[i]); @@ -242,7 +242,7 @@ UNIT_TEST(SmallMap_Benchmark3) // 3. Run unordered_map. { - base::Timer timer; + base::HighResTimer timer; for (auto i : indices) sum1 += uMap.find(keys[i])->second; t1 = timer.ElapsedMilliseconds(); @@ -250,7 +250,7 @@ UNIT_TEST(SmallMap_Benchmark3) // 4. Run SmallMap. { - base::Timer timer; + base::HighResTimer timer; for (auto i : indices) sum2 += *sMap.Find(keys[i]); t2 = timer.ElapsedMilliseconds(); @@ -258,7 +258,7 @@ UNIT_TEST(SmallMap_Benchmark3) // 5. Run SmallMapBase. { - base::Timer timer; + base::HighResTimer timer; for (auto i : indices) sum3 += *sbMap.Find(keys[i]); t3 = timer.ElapsedMilliseconds(); diff --git a/base/timer.cpp b/base/timer.cpp index 6b4a0a3e2a..8e7c36453f 100644 --- a/base/timer.cpp +++ b/base/timer.cpp @@ -19,12 +19,6 @@ namespace base { -Timer::Timer(bool start/* = true*/) -{ - if (start) - Reset(); -} - // static double Timer::LocalTime() { @@ -171,32 +165,6 @@ time_t StringToTimestamp(std::string const & s) return res; } -HighResTimer::HighResTimer(bool start/* = true*/) -{ - if (start) - Reset(); -} - -void HighResTimer::Reset() -{ - m_start = std::chrono::high_resolution_clock::now(); -} - -uint64_t HighResTimer::ElapsedNano() const -{ - return std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_start).count(); -} - -uint64_t HighResTimer::ElapsedMillis() const -{ - return std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_start).count(); -} - -double HighResTimer::ElapsedSeconds() const -{ - return std::chrono::duration_cast>(std::chrono::high_resolution_clock::now() - m_start).count(); -} - time_t SecondsSinceEpochToTimeT(uint64_t secondsSinceEpoch) { std::chrono::time_point const tpoint{std::chrono::seconds(secondsSinceEpoch)}; @@ -220,7 +188,7 @@ ScopedTimerWithLog::~ScopedTimerWithLog() { case Measure::MilliSeconds: { - LOG(LINFO, (m_name, "time:", m_timer.ElapsedMillis(), "ms")); + LOG(LINFO, (m_name, "time:", m_timer.ElapsedMilliseconds(), "ms")); return; } case Measure::Seconds: diff --git a/base/timer.hpp b/base/timer.hpp index d918265751..49499e1fad 100644 --- a/base/timer.hpp +++ b/base/timer.hpp @@ -7,19 +7,22 @@ namespace base { -/// Cross platform timer -class Timer +namespace impl { - std::chrono::steady_clock::time_point m_startTime; +template class StdTimer +{ + typename ClockT::time_point m_startTime; public: - explicit Timer(bool start = true); - - /// @return current UTC time in seconds, elapsed from 1970. - static double LocalTime(); + explicit StdTimer(bool start = true) + { + if (start) + Reset(); + } + using DurationT = typename ClockT::duration; /// @return Elapsed time from start (@see Reset). - inline std::chrono::steady_clock::duration TimeElapsed() const { return std::chrono::steady_clock::now() - m_startTime; } + inline DurationT TimeElapsed() const { return ClockT::now() - m_startTime; } template inline Duration TimeElapsedAs() const @@ -31,7 +34,21 @@ public: inline uint64_t ElapsedMilliseconds() const { return TimeElapsedAs().count(); } inline uint64_t ElapsedNanoseconds() const { return TimeElapsedAs().count(); } - inline void Reset() { m_startTime = std::chrono::steady_clock::now(); } + inline void Reset() { m_startTime = ClockT::now(); } +}; +} // namespace impl + + +/// Cross platform timer +class Timer : public impl::StdTimer +{ + using BaseT = impl::StdTimer; + +public: + using BaseT::BaseT; + + /// @return current UTC time in seconds, elapsed from 1970. + static double LocalTime(); }; std::string FormatCurrentTime(); @@ -62,19 +79,7 @@ time_t StringToTimestamp(std::string const & s); /// High resolution timer to use in comparison tests. -class HighResTimer -{ - typedef std::chrono::high_resolution_clock::time_point PointT; - PointT m_start; - -public: - explicit HighResTimer(bool start = true); - - void Reset(); - uint64_t ElapsedNano() const; - uint64_t ElapsedMillis() const; - double ElapsedSeconds() const; -}; +using HighResTimer = impl::StdTimer; class ScopedTimerWithLog { diff --git a/routing/index_graph.cpp b/routing/index_graph.cpp index 6ab2fde34f..92dd7a3df4 100644 --- a/routing/index_graph.cpp +++ b/routing/index_graph.cpp @@ -226,7 +226,7 @@ void IndexGraph::SetRestrictions(RestrictionVec && restrictions) m_restrictionsBackward[restriction.front()].emplace_back(next(restriction.begin()), restriction.end()); } - LOG(LDEBUG, ("Restrictions are loaded in:", timer.ElapsedNano() / 1e6, "ms")); + LOG(LDEBUG, ("Restrictions are loaded in:", timer.ElapsedMilliseconds(), "ms")); } void IndexGraph::SetUTurnRestrictions(vector && noUTurnRestrictions) diff --git a/routing/routes_builder/routes_builder_tool/utils.cpp b/routing/routes_builder/routes_builder_tool/utils.cpp index 74d3bf4184..bfe4866753 100644 --- a/routing/routes_builder/routes_builder_tool/utils.cpp +++ b/routing/routes_builder/routes_builder_tool/utils.cpp @@ -25,6 +25,11 @@ #include #include + +namespace routing +{ +namespace routes_builder +{ using namespace routing_quality; namespace @@ -58,10 +63,6 @@ routing::VehicleType ConvertVehicleTypeFromString(std::string const & str) } } // namespace -namespace routing -{ -namespace routes_builder -{ void BuildRoutes(std::string const & routesPath, std::string const & dumpPath, uint64_t startFrom, @@ -180,8 +181,9 @@ void BuildRoutesWithApi(std::unique_ptr routingApi, size_t rps = 0; base::HighResTimer timer; - auto const getElapsedMilliSeconds = [&timer]() { - double ms = timer.ElapsedNano() / 1e6; + auto const getElapsedMilliSeconds = [&timer]() + { + auto const ms = timer.ElapsedMilliseconds(); LOG(LDEBUG, ("Elapsed:", ms, "ms")); return ms; }; @@ -191,9 +193,10 @@ void BuildRoutesWithApi(std::unique_ptr routingApi, timer.Reset(); }; - auto const sleepIfNeed = [&]() { - double constexpr kMsInSecond = 1000.0; - if (getElapsedMilliSeconds() > kMsInSecond) + auto const sleepIfNeed = [&]() + { + // Greater than 1 second. + if (getElapsedMilliSeconds() > 1000) { drop(); return; @@ -210,7 +213,8 @@ void BuildRoutesWithApi(std::unique_ptr routingApi, size_t count = 0; size_t prevDumpedNumber = 0; - auto const dump = [&]() { + auto const dump = [&]() + { for (size_t i = 0; i < count; ++i) { std::string filepath = diff --git a/search/search_params.hpp b/search/search_params.hpp index f7070385d0..63323c21bb 100644 --- a/search/search_params.hpp +++ b/search/search_params.hpp @@ -21,18 +21,18 @@ class Tracer; struct SearchParams { - inline static size_t const kDefaultNumBookmarksResults = 1000; - inline static size_t const kDefaultBatchSizeEverywhere = 10; - inline static size_t const kDefaultNumResultsEverywhere = 30; - inline static size_t const kDefaultNumResultsInViewport = 200; - inline static size_t const kPreResultsCount = 200; - inline static double const kDefaultStreetSearchRadiusM = 8e4; - inline static double const kDefaultVillageSearchRadiusM = 2e5; - // TODO: Short timeouts leads to a non-working search on slow devices. Design a better solution. - inline static std::chrono::steady_clock::duration const kDefaultTimeout = - std::chrono::seconds(8); - inline static std::chrono::steady_clock::duration const kDefaultDesktopTimeout = - std::chrono::seconds(8); + static size_t constexpr kDefaultNumBookmarksResults = 1000; + static size_t constexpr kDefaultBatchSizeEverywhere = 10; + static size_t constexpr kDefaultNumResultsEverywhere = 30; + static size_t constexpr kDefaultNumResultsInViewport = 200; + static size_t constexpr kPreResultsCount = 200; + static double constexpr kDefaultStreetSearchRadiusM = 8e4; + static double constexpr kDefaultVillageSearchRadiusM = 2e5; + + using TimeDurationT = base::Timer::DurationT; + /// @todo Short timeouts leads to a non-working search on slow devices. Design a better solution. + static TimeDurationT constexpr kDefaultTimeout = std::chrono::seconds(8); + static TimeDurationT constexpr kDefaultDesktopTimeout = std::chrono::seconds(8); using OnStarted = std::function; using OnResults = std::function; @@ -95,7 +95,7 @@ struct SearchParams bookmarks::GroupId m_bookmarksGroupId = bookmarks::kInvalidGroupId; // Amount of time after which the search is aborted. - std::chrono::steady_clock::duration m_timeout = kDefaultTimeout; + TimeDurationT m_timeout = kDefaultTimeout; std::shared_ptr m_tracer; }; diff --git a/search/search_tests_support/test_search_request.cpp b/search/search_tests_support/test_search_request.cpp index 657ca7b99c..676a303069 100644 --- a/search/search_tests_support/test_search_request.cpp +++ b/search/search_tests_support/test_search_request.cpp @@ -60,7 +60,7 @@ void TestSearchRequest::Run() Wait(); } -steady_clock::duration TestSearchRequest::ResponseTime() const +TestSearchRequest::TimeDurationT TestSearchRequest::ResponseTime() const { lock_guard lock(m_mu); CHECK(m_done, ("This function may be called only when request is processed.")); diff --git a/search/search_tests_support/test_search_request.hpp b/search/search_tests_support/test_search_request.hpp index d5a92d0b61..5c5dca41a7 100644 --- a/search/search_tests_support/test_search_request.hpp +++ b/search/search_tests_support/test_search_request.hpp @@ -25,8 +25,8 @@ class TestSearchEngine; class TestSearchRequest { public: - inline static double const kDefaultTestStreetSearchRadiusM = 2e7; - inline static double const kDefaultTestVillageSearchRadiusM = 2e7; + static double constexpr kDefaultTestStreetSearchRadiusM = 2e7; + static double constexpr kDefaultTestVillageSearchRadiusM = 2e7; TestSearchRequest(TestSearchEngine & engine, std::string const & query, std::string const & locale, Mode mode, m2::RectD const & viewport); @@ -42,7 +42,8 @@ public: void Wait(); // Call these functions only after call to Wait(). - std::chrono::steady_clock::duration ResponseTime() const; + using TimeDurationT = base::Timer::DurationT; + TimeDurationT ResponseTime() const; std::vector const & Results() const; protected: @@ -67,8 +68,7 @@ protected: bool m_done = false; base::Timer m_timer; - std::chrono::steady_clock::duration m_startTime; - std::chrono::steady_clock::duration m_endTime; + TimeDurationT m_startTime, m_endTime; TestSearchEngine & m_engine; SearchParams m_params; diff --git a/testing/testingmain.cpp b/testing/testingmain.cpp index 810f78279e..8d32817eae 100644 --- a/testing/testingmain.cpp +++ b/testing/testingmain.cpp @@ -277,7 +277,7 @@ int main(int argc, char * argv[]) } g_lastTestOK = true; - uint64_t const elapsed = timer.ElapsedNano(); + uint64_t const elapsed = timer.ElapsedNanoseconds(); LOG(LINFO, ("Test took", elapsed / 1000000, "ms\n")); } diff --git a/tracking/tracking_tests/archival_reporter_tests.cpp b/tracking/tracking_tests/archival_reporter_tests.cpp index ae76c71e91..824e7bc29f 100644 --- a/tracking/tracking_tests/archival_reporter_tests.cpp +++ b/tracking/tracking_tests/archival_reporter_tests.cpp @@ -66,26 +66,27 @@ UNIT_TEST(PacketCar_OperationsConsistency) location::GpsInfo point = GetStartingPoint(); traffic::SpeedGroup sg = traffic::SpeedGroup::G0; - base::HighResTimer timerStart; + base::HighResTimer timer; + for (size_t i = 0; i < kItemsForDump; ++i) { archive.Add(point, sg); UpdateLocation(point); UpdateSpeedGroup(sg); } + auto const track = archive.Extract(); - base::HighResTimer timerStartSaving; + LOG(LINFO, ("Duration of dumping", timer.ElapsedMilliseconds(), "ms")); + + timer.Reset(); std::string const fileName = "archival_reporter_car.track"; { FileWriter writer(fileName); CHECK(archive.Write(writer), ()); } - LOG(LINFO, ("Duration of serializing", - timerStart.ElapsedMillis() - timerStartSaving.ElapsedMillis(), "ms")); - LOG(LINFO, - ("Duration of dumping", timerStart.ElapsedMillis() - timerStartSaving.ElapsedMillis(), "ms")); + LOG(LINFO, ("Duration of serializing", timer.ElapsedMilliseconds(), "ms")); uint64_t sizeBytes; CHECK(GetPlatform().GetFileSizeByFullPath(fileName, sizeBytes), ()); -- 2.45.3 From 39073aad1bee8d0f053299ed417544062b14df2f Mon Sep 17 00:00:00 2001 From: Viktor Govako Date: Sun, 12 Jun 2022 15:00:33 +0300 Subject: [PATCH 2/4] [search] Added SearchParams.m_categorialRequest for *pure* category results. Signed-off-by: Viktor Govako --- .../jni/com/mapswithme/maps/SearchEngine.cpp | 10 +++-- .../mapswithme/maps/routing/SearchWheel.java | 3 +- .../mapswithme/maps/search/SearchEngine.java | 27 ++++++------ .../maps/search/SearchFragment.java | 25 +++++------ .../maps/widget/SearchToolbarController.java | 6 ++- .../CarPlay/MWMCarPlaySearchService.mm | 3 +- .../MWMMapViewControlsManager.mm | 2 +- .../Views/MWMNavigationInfoView.mm | 3 +- iphone/Maps/Core/Search/MWMSearch.h | 2 +- iphone/Maps/Core/Search/MWMSearch.mm | 6 ++- iphone/Maps/UI/Search/MWMSearchManager.h | 2 +- iphone/Maps/UI/Search/MWMSearchManager.mm | 15 ++++--- .../TableView/MWMSearchTableViewController.mm | 3 +- .../Search/Tabs/MWMSearchTabbedViewProtocol.h | 2 +- .../Search/Tabs/SearchTabViewController.swift | 7 ++-- map/everywhere_search_params.hpp | 1 + map/search_api.cpp | 2 + map/viewport_search_params.hpp | 1 + search/processor.cpp | 41 ++++++++++-------- search/processor.hpp | 2 +- .../processor_test.cpp | 40 ++++++++++++++---- .../search_integration_tests/smoke_test.cpp | 33 ++++++--------- search/search_params.hpp | 5 ++- search/search_tests_support/helpers.cpp | 42 +++++++++++-------- search/search_tests_support/helpers.hpp | 1 + .../test_search_engine.cpp | 2 +- .../test_search_engine.hpp | 4 +- .../test_search_request.hpp | 2 + 28 files changed, 174 insertions(+), 118 deletions(-) diff --git a/android/jni/com/mapswithme/maps/SearchEngine.cpp b/android/jni/com/mapswithme/maps/SearchEngine.cpp index edb87542d5..3c86bbd360 100644 --- a/android/jni/com/mapswithme/maps/SearchEngine.cpp +++ b/android/jni/com/mapswithme/maps/SearchEngine.cpp @@ -290,12 +290,13 @@ extern "C" } JNIEXPORT jboolean JNICALL Java_com_mapswithme_maps_search_SearchEngine_nativeRunSearch( - JNIEnv * env, jclass clazz, jbyteArray bytes, jstring lang, jlong timestamp, - jboolean hasPosition, jdouble lat, jdouble lon) + JNIEnv * env, jclass clazz, jbyteArray bytes, jboolean isCategory, + jstring lang, jlong timestamp, jboolean hasPosition, jdouble lat, jdouble lon) { search::EverywhereSearchParams params; params.m_query = jni::ToNativeString(env, bytes); params.m_inputLocale = jni::ToNativeString(env, lang); + params.m_isCategory = isCategory; params.m_onResults = bind(&OnResults, _1, _2, timestamp, false, hasPosition, lat, lon); bool const searchStarted = g_framework->NativeFramework()->GetSearchAPI().SearchEverywhere(params); if (searchStarted) @@ -304,12 +305,13 @@ extern "C" } JNIEXPORT void JNICALL Java_com_mapswithme_maps_search_SearchEngine_nativeRunInteractiveSearch( - JNIEnv * env, jclass clazz, jbyteArray bytes, jstring lang, jlong timestamp, - jboolean isMapAndTable) + JNIEnv * env, jclass clazz, jbyteArray bytes, jboolean isCategory, + jstring lang, jlong timestamp, jboolean isMapAndTable) { search::ViewportSearchParams vparams; vparams.m_query = jni::ToNativeString(env, bytes); vparams.m_inputLocale = jni::ToNativeString(env, lang); + vparams.m_isCategory = isCategory; // TODO (@alexzatsepin): set up vparams.m_onCompleted here and use // HotelsClassifier for hotel queries detection. diff --git a/android/src/com/mapswithme/maps/routing/SearchWheel.java b/android/src/com/mapswithme/maps/routing/SearchWheel.java index 5241234b95..4085920c5c 100644 --- a/android/src/com/mapswithme/maps/routing/SearchWheel.java +++ b/android/src/com/mapswithme/maps/routing/SearchWheel.java @@ -280,7 +280,8 @@ class SearchWheel implements View.OnClickListener { mCurrentOption = searchOption; final String query = mFrame.getContext().getString(searchOption.mQueryId); - SearchEngine.INSTANCE.searchInteractive(mFrame.getContext(), query, System.nanoTime(), false /* isMapAndTable */); + // Category request from navigation search wheel. + SearchEngine.INSTANCE.searchInteractive(mFrame.getContext(), query, true, System.nanoTime(), false); SearchEngine.INSTANCE.setQuery(query); refreshSearchButtonImage(); toggleSearchLayout(); diff --git a/android/src/com/mapswithme/maps/search/SearchEngine.java b/android/src/com/mapswithme/maps/search/SearchEngine.java index ae2c97197a..94078ac24f 100644 --- a/android/src/com/mapswithme/maps/search/SearchEngine.java +++ b/android/src/com/mapswithme/maps/search/SearchEngine.java @@ -127,25 +127,26 @@ public enum SearchEngine implements NativeSearchListener, * @return whether search was actually started. */ @MainThread - public boolean search(@NonNull Context context, String query, long timestamp, boolean hasLocation, - double lat, double lon) + public boolean search(@NonNull Context context, String query, boolean isCategory, + long timestamp, boolean hasLocation, double lat, double lon) { - return nativeRunSearch(query.getBytes(StandardCharsets.UTF_8), Language.getKeyboardLocale(context), - timestamp, hasLocation, lat, lon); + return nativeRunSearch(query.getBytes(StandardCharsets.UTF_8), isCategory, + Language.getKeyboardLocale(context), timestamp, hasLocation, lat, lon); } @MainThread - public void searchInteractive(@NonNull String query, @NonNull String locale, long timestamp, - boolean isMapAndTable) + public void searchInteractive(@NonNull String query, boolean isCategory, @NonNull String locale, + long timestamp, boolean isMapAndTable) { - nativeRunInteractiveSearch(query.getBytes(StandardCharsets.UTF_8), locale, timestamp, isMapAndTable); + nativeRunInteractiveSearch(query.getBytes(StandardCharsets.UTF_8), isCategory, + locale, timestamp, isMapAndTable); } @MainThread - public void searchInteractive(@NonNull Context context, @NonNull String query, long timestamp, - boolean isMapAndTable) + public void searchInteractive(@NonNull Context context, @NonNull String query, boolean isCategory, + long timestamp, boolean isMapAndTable) { - searchInteractive(query, Language.getKeyboardLocale(context), timestamp, isMapAndTable); + searchInteractive(query, isCategory, Language.getKeyboardLocale(context), timestamp, isMapAndTable); } @MainThread @@ -223,13 +224,15 @@ public enum SearchEngine implements NativeSearchListener, /** * @param bytes utf-8 formatted bytes of query. */ - private static native boolean nativeRunSearch(byte[] bytes, String language, long timestamp, boolean hasLocation, + private static native boolean nativeRunSearch(byte[] bytes, boolean isCategory, + String language, long timestamp, boolean hasLocation, double lat, double lon); /** * @param bytes utf-8 formatted query bytes */ - private static native void nativeRunInteractiveSearch(byte[] bytes, String language, long timestamp, + private static native void nativeRunInteractiveSearch(byte[] bytes, boolean isCategory, + String language, long timestamp, boolean isMapAndTable); /** diff --git a/android/src/com/mapswithme/maps/search/SearchFragment.java b/android/src/com/mapswithme/maps/search/SearchFragment.java index 9529aed4dc..7e6a0829f3 100644 --- a/android/src/com/mapswithme/maps/search/SearchFragment.java +++ b/android/src/com/mapswithme/maps/search/SearchFragment.java @@ -348,15 +348,9 @@ public class SearchFragment extends BaseMwmFragment super.onDestroy(); } - private String getQuery() - { - return mToolbarController.getQuery(); - } - - void setQuery(String text) - { - mToolbarController.setQuery(text); - } + private String getQuery() { return mToolbarController.getQuery(); } + private boolean isCategory() { return mToolbarController.isCategory(); } + void setQuery(String text) { mToolbarController.setQuery(text); } private void readArguments() { @@ -436,12 +430,12 @@ public class SearchFragment extends BaseMwmFragment mLastQueryTimestamp = System.nanoTime(); SearchEngine.INSTANCE.searchInteractive( - query, !TextUtils.isEmpty(mInitialLocale) + query, isCategory(), !TextUtils.isEmpty(mInitialLocale) ? mInitialLocale : com.mapswithme.util.Language.getKeyboardLocale(requireContext()), mLastQueryTimestamp, false /* isMapAndTable */); + SearchEngine.INSTANCE.setQuery(query); Utils.navigateToParent(getActivity()); - } private void onSearchEnd() @@ -479,12 +473,13 @@ public class SearchFragment extends BaseMwmFragment mLastQueryTimestamp = System.nanoTime(); if (isTabletSearch()) { - SearchEngine.INSTANCE.searchInteractive(requireContext(), getQuery(), mLastQueryTimestamp, true /* isMapAndTable */); + SearchEngine.INSTANCE.searchInteractive(requireContext(), getQuery(), isCategory(), + mLastQueryTimestamp, true /* isMapAndTable */); } else { - if (!SearchEngine.INSTANCE.search(requireContext(), getQuery(), mLastQueryTimestamp, mLastPosition.valid, - mLastPosition.lat, mLastPosition.lon)) + if (!SearchEngine.INSTANCE.search(requireContext(), getQuery(), isCategory(), + mLastQueryTimestamp, mLastPosition.valid, mLastPosition.lat, mLastPosition.lon)) { return; } @@ -518,7 +513,7 @@ public class SearchFragment extends BaseMwmFragment @Override public void onSearchCategorySelected(@Nullable String category) { - mToolbarController.setQuery(category); + mToolbarController.setQuery(category, true); } private void refreshSearchResults(@NonNull SearchResult[] results) diff --git a/android/src/com/mapswithme/maps/widget/SearchToolbarController.java b/android/src/com/mapswithme/maps/widget/SearchToolbarController.java index 90b9d164f1..12db4194de 100644 --- a/android/src/com/mapswithme/maps/widget/SearchToolbarController.java +++ b/android/src/com/mapswithme/maps/widget/SearchToolbarController.java @@ -31,6 +31,7 @@ public class SearchToolbarController extends ToolbarController implements View.O private final View mBack; @NonNull private final EditText mQuery; + private boolean mFromCategory = false; @NonNull private final View mProgress; @NonNull @@ -157,13 +158,16 @@ public class SearchToolbarController extends ToolbarController implements View.O { return (UiUtils.isVisible(mSearchContainer) ? mQuery.getText().toString() : ""); } + public boolean isCategory() { return mFromCategory; } - public void setQuery(CharSequence query) + public void setQuery(CharSequence query, boolean fromCategory) { + mFromCategory = fromCategory; mQuery.setText(query); if (!TextUtils.isEmpty(query)) mQuery.setSelection(query.length()); } + public void setQuery(CharSequence query) { setQuery(query, false); } public void clear() { diff --git a/iphone/Maps/Classes/CarPlay/MWMCarPlaySearchService.mm b/iphone/Maps/Classes/CarPlay/MWMCarPlaySearchService.mm index 2f13ff609c..b8b4f83aa9 100644 --- a/iphone/Maps/Classes/CarPlay/MWMCarPlaySearchService.mm +++ b/iphone/Maps/Classes/CarPlay/MWMCarPlaySearchService.mm @@ -29,7 +29,8 @@ API_AVAILABLE(ios(12.0)) self.inputLocale = inputLocale; self.lastResults = @[]; self.completionHandler = completionHandler; - [MWMSearch searchQuery:text forInputLocale:inputLocale]; + /// @todo Didn't find pure category request in CarPlay. + [MWMSearch searchQuery:text forInputLocale:inputLocale withCategory:NO]; } - (void)saveLastQuery { diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm index 6dcc05838e..74107c49a5 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm @@ -106,7 +106,7 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; return NO; self.searchManager.state = MWMSearchManagerStateTableSearch; - [self.searchManager searchText:text forInputLocale:locale]; + [self.searchManager searchText:text forInputLocale:locale withCategory:NO]; return YES; } diff --git a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm index 7c1bdbe05e..02f879ed97 100644 --- a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm +++ b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm @@ -193,7 +193,8 @@ BOOL defaultOrientation(CGSize const &size) { [MWMSearch setSearchOnMap:YES]; NSString *query = [kSearchButtonRequest.at(state) stringByAppendingString:@" "]; NSString *locale = [[AppInfo sharedInfo] languageId]; - [MWMSearch searchQuery:query forInputLocale:locale]; + // Category request from navigation search wheel. + [MWMSearch searchQuery:query forInputLocale:locale withCategory:YES]; [self setSearchState:state animated:YES]; }; diff --git a/iphone/Maps/Core/Search/MWMSearch.h b/iphone/Maps/Core/Search/MWMSearch.h index 3bc8ea3e0b..edc489da2a 100644 --- a/iphone/Maps/Core/Search/MWMSearch.h +++ b/iphone/Maps/Core/Search/MWMSearch.h @@ -14,7 +14,7 @@ struct ProductInfo; + (void)removeObserver:(id)observer; + (void)saveQuery:(NSString *)query forInputLocale:(NSString *)inputLocale; -+ (void)searchQuery:(NSString *)query forInputLocale:(NSString *)inputLocale; ++ (void)searchQuery:(NSString *)query forInputLocale:(NSString *)inputLocale withCategory:(BOOL)isCategory; + (void)showResult:(search::Result const &)result; diff --git a/iphone/Maps/Core/Search/MWMSearch.mm b/iphone/Maps/Core/Search/MWMSearch.mm index 4b99ddb60a..b16c51382f 100644 --- a/iphone/Maps/Core/Search/MWMSearch.mm +++ b/iphone/Maps/Core/Search/MWMSearch.mm @@ -132,7 +132,7 @@ using Observers = NSHashTable; GetFramework().GetSearchAPI().SaveSearchQuery(make_pair(locale, text)); } -+ (void)searchQuery:(NSString *)query forInputLocale:(NSString *)inputLocale { ++ (void)searchQuery:(NSString *)query forInputLocale:(NSString *)inputLocale withCategory:(BOOL)isCategory { if (!query) return; @@ -142,11 +142,15 @@ using Observers = NSHashTable; manager->m_everywhereParams.m_inputLocale = locale; manager->m_viewportParams.m_inputLocale = locale; } + manager.lastQuery = query.precomposedStringWithCompatibilityMapping; std::string const text = manager.lastQuery.UTF8String; manager->m_everywhereParams.m_query = text; manager->m_viewportParams.m_query = text; manager.textChanged = YES; + + manager->m_everywhereParams.m_isCategory = manager->m_viewportParams.m_isCategory = (isCategory == YES); + [manager update]; } diff --git a/iphone/Maps/UI/Search/MWMSearchManager.h b/iphone/Maps/UI/Search/MWMSearchManager.h index 0018d916e6..7b5ed4346e 100644 --- a/iphone/Maps/UI/Search/MWMSearchManager.h +++ b/iphone/Maps/UI/Search/MWMSearchManager.h @@ -22,7 +22,7 @@ typedef NS_ENUM(NSInteger, MWMSearchManagerRoutingTooltipSearch) { @property(nonnull, nonatomic) IBOutletCollection(UIView) NSArray *topViews; -- (void)searchText:(nonnull NSString *)text forInputLocale:(nullable NSString *)locale; +- (void)searchText:(nonnull NSString *)text forInputLocale:(nullable NSString *)locale withCategory:(BOOL)isCategory; #pragma mark - Layout diff --git a/iphone/Maps/UI/Search/MWMSearchManager.mm b/iphone/Maps/UI/Search/MWMSearchManager.mm index 3b8d834c18..d78d39a872 100644 --- a/iphone/Maps/UI/Search/MWMSearchManager.mm +++ b/iphone/Maps/UI/Search/MWMSearchManager.mm @@ -97,7 +97,7 @@ using Observers = NSHashTable; NSString *text = textField.text; if (text.length > 0) { [self beginSearch]; - [MWMSearch searchQuery:text forInputLocale:textField.textInputMode.primaryLanguage]; + [MWMSearch searchQuery:text forInputLocale:textField.textInputMode.primaryLanguage withCategory:NO]; } else { [self endSearch]; } @@ -135,11 +135,11 @@ using Observers = NSHashTable; #pragma mark - MWMSearchTabbedViewProtocol -- (void)searchText:(NSString *)text forInputLocale:(NSString *)locale { +- (void)searchText:(NSString *)text forInputLocale:(NSString *)locale withCategory:(BOOL)isCategory { [self beginSearch]; self.searchTextField.text = text; NSString *inputLocale = locale ?: self.searchTextField.textInputMode.primaryLanguage; - [MWMSearch searchQuery:text forInputLocale:inputLocale]; + [MWMSearch searchQuery:text forInputLocale:inputLocale withCategory:isCategory]; } - (void)dismissKeyboard { @@ -176,7 +176,7 @@ using Observers = NSHashTable; if (self.state == MWMSearchManagerStateTableSearch || self.state == MWMSearchManagerStateMapSearch) { NSString *text = self.searchTextField.text; if (text.length != 0) - [MWMSearch searchQuery:text forInputLocale:self.searchTextField.textInputMode.primaryLanguage]; + [MWMSearch searchQuery:text forInputLocale:self.searchTextField.textInputMode.primaryLanguage withCategory:NO]; } } @@ -413,8 +413,11 @@ using Observers = NSHashTable; } } -- (void)searchTabController:(MWMSearchTabViewController *)viewController didSearch:(NSString *)didSearch { - [self searchText:didSearch forInputLocale:[[AppInfo sharedInfo] languageId]]; +- (void)searchTabController:(MWMSearchTabViewController *)viewController + didSearch:(NSString *)didSearch + withCategory:(BOOL)isCategory +{ + [self searchText:didSearch forInputLocale:[[AppInfo sharedInfo] languageId] withCategory:isCategory]; } - (MWMSearchTableViewController *)tableViewController { diff --git a/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm b/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm index ed72af127f..0ce971bfe1 100644 --- a/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm +++ b/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm @@ -129,7 +129,8 @@ NSString *GetLocalizedTypeName(search::Result const &result) { case MWMSearchItemTypeSuggestion: { auto const &suggestion = [MWMSearch resultWithContainerIndex:containerIndex]; NSString *suggestionString = @(suggestion.GetSuggestionString().c_str()); - [delegate searchText:suggestionString forInputLocale:nil]; + /// @todo Pass withCategory:YES if we tap on category suggestion (not street or city)? + [delegate searchText:suggestionString forInputLocale:nil withCategory:NO]; } } } diff --git a/iphone/Maps/UI/Search/Tabs/MWMSearchTabbedViewProtocol.h b/iphone/Maps/UI/Search/Tabs/MWMSearchTabbedViewProtocol.h index aca5de9643..98147899b9 100644 --- a/iphone/Maps/UI/Search/Tabs/MWMSearchTabbedViewProtocol.h +++ b/iphone/Maps/UI/Search/Tabs/MWMSearchTabbedViewProtocol.h @@ -6,7 +6,7 @@ @property(nonatomic) MWMSearchManagerState state; -- (void)searchText:(NSString *)text forInputLocale:(NSString *)locale; +- (void)searchText:(NSString *)text forInputLocale:(NSString *)locale withCategory:(BOOL)isCategory; - (void)dismissKeyboard; @end diff --git a/iphone/Maps/UI/Search/Tabs/SearchTabViewController.swift b/iphone/Maps/UI/Search/Tabs/SearchTabViewController.swift index 3ccb06f370..5d60214d05 100644 --- a/iphone/Maps/UI/Search/Tabs/SearchTabViewController.swift +++ b/iphone/Maps/UI/Search/Tabs/SearchTabViewController.swift @@ -1,6 +1,7 @@ +import CoreFoundation @objc(MWMSearchTabViewControllerDelegate) protocol SearchTabViewControllerDelegate: AnyObject { - func searchTabController(_ viewContoller: SearchTabViewController, didSearch: String) + func searchTabController(_ viewContoller: SearchTabViewController, didSearch: String, withCategory: Bool) } @objc(MWMSearchTabViewController) @@ -53,13 +54,13 @@ extension SearchTabViewController: SearchCategoriesViewControllerDelegate { func categoriesViewController(_ viewController: SearchCategoriesViewController, didSelect category: String) { let query = L(category) + " " - delegate?.searchTabController(self, didSearch: query) + delegate?.searchTabController(self, didSearch: query, withCategory: true) } } extension SearchTabViewController: SearchHistoryViewControllerDelegate { func searchHistoryViewController(_ viewController: SearchHistoryViewController, didSelect query: String) { - delegate?.searchTabController(self, didSearch: query) + delegate?.searchTabController(self, didSearch: query, withCategory: false) } } diff --git a/map/everywhere_search_params.hpp b/map/everywhere_search_params.hpp index d26a0a7221..7a98bf6a8c 100644 --- a/map/everywhere_search_params.hpp +++ b/map/everywhere_search_params.hpp @@ -21,6 +21,7 @@ struct EverywhereSearchParams std::string m_query; std::string m_inputLocale; std::optional m_timeout; + bool m_isCategory = false; OnResults m_onResults; }; diff --git a/map/search_api.cpp b/map/search_api.cpp index 62ca2fbfbb..f11d6c5939 100644 --- a/map/search_api.cpp +++ b/map/search_api.cpp @@ -184,6 +184,7 @@ bool SearchAPI::SearchEverywhere(EverywhereSearchParams const & params) p.m_suggestsEnabled = true; p.m_needAddress = true; p.m_needHighlighting = true; + p.m_categorialRequest = params.m_isCategory; if (params.m_timeout) p.m_timeout = *params.m_timeout; @@ -211,6 +212,7 @@ bool SearchAPI::SearchInViewport(ViewportSearchParams const & params) p.m_suggestsEnabled = false; p.m_needAddress = false; p.m_needHighlighting = false; + p.m_categorialRequest = params.m_isCategory; if (params.m_timeout) p.m_timeout = *params.m_timeout; diff --git a/map/viewport_search_params.hpp b/map/viewport_search_params.hpp index c13c9199d4..87b23619a1 100644 --- a/map/viewport_search_params.hpp +++ b/map/viewport_search_params.hpp @@ -17,6 +17,7 @@ struct ViewportSearchParams std::string m_query; std::string m_inputLocale; std::optional m_timeout; + bool m_isCategory = false; OnStarted m_onStarted; OnCompleted m_onCompleted; diff --git a/search/processor.cpp b/search/processor.cpp index 1da98acdaf..3f67a94166 100644 --- a/search/processor.cpp +++ b/search/processor.cpp @@ -253,7 +253,7 @@ void Processor::SetInputLocale(string const & locale) m_inputLocaleCode = CategoriesHolder::MapLocaleToInteger(locale); } -void Processor::SetQuery(string const & query) +void Processor::SetQuery(string const & query, bool categorialRequest /* = false */) { m_query = query; m_tokens.clear(); @@ -319,20 +319,23 @@ void Processor::SetQuery(string const & query) // Get preferred types to show in results. m_preferredTypes.clear(); auto const tokenSlice = QuerySliceOnRawStrings(m_tokens, m_prefix); - m_isCategorialRequest = FillCategories(tokenSlice, GetCategoryLocales(), m_categories, m_preferredTypes); - if (!m_isCategorialRequest) + m_isCategorialRequest = categorialRequest; + + auto const locales = GetCategoryLocales(); + if (!FillCategories(tokenSlice, locales, m_categories, m_preferredTypes)) { // Try to match query to cuisine categories. - bool const isCuisineRequest = FillCategories( - tokenSlice, GetCategoryLocales(), GetDefaultCuisineCategories(), m_cuisineTypes); - - if (isCuisineRequest) + if (FillCategories(tokenSlice, locales, GetDefaultCuisineCategories(), m_cuisineTypes)) { + /// @todo What if I'd like to find "Burger" street? @see "BurgerStreet" test. m_isCategorialRequest = true; m_preferredTypes = ftypes::IsEatChecker::Instance().GetTypes(); } + } + if (!m_isCategorialRequest) + { // Assign tokens and prefix to scorer. m_keywordsScorer.SetKeywords(m_tokens.data(), m_tokens.size(), m_prefix); @@ -601,7 +604,7 @@ void Processor::Search(SearchParams const & params) SetInputLocale(params.m_inputLocale); - SetQuery(params.m_query); + SetQuery(params.m_query, params.m_categorialRequest); SetViewport(viewport); // Used to store the earliest available cancellation status: @@ -802,12 +805,9 @@ void Processor::InitParams(QueryParams & params) const else params.InitWithPrefix(m_tokens.begin(), m_tokens.end(), m_prefix); - // Add names of categories (and synonyms). Classificator const & c = classif(); - auto addCategorySynonyms = [&](size_t i, uint32_t t) { - uint32_t const index = c.GetIndexForType(t); - params.GetTypeIndices(i).push_back(index); - }; + + // Add names of categories (and synonyms). auto const tokenSlice = QuerySliceOnRawStrings(m_tokens, m_prefix); params.SetCategorialRequest(m_isCategorialRequest); if (m_isCategorialRequest) @@ -822,11 +822,14 @@ void Processor::InitParams(QueryParams & params) const else { // todo(@m, @y). Shall we match prefix tokens for categories? - ForEachCategoryTypeFuzzy(tokenSlice, addCategorySynonyms); + ForEachCategoryTypeFuzzy(tokenSlice, [&c, ¶ms](size_t i, uint32_t t) + { + uint32_t const index = c.GetIndexForType(t); + params.GetTypeIndices(i).push_back(index); + }); } - // Remove all type indices for streets, as they're considired - // individually. + // Remove all type indices for streets, as they're considired individually. for (size_t i = 0; i < params.GetNumTokens(); ++i) { auto & token = params.GetToken(i); @@ -837,8 +840,10 @@ void Processor::InitParams(QueryParams & params) const for (size_t i = 0; i < params.GetNumTokens(); ++i) base::SortUnique(params.GetTypeIndices(i)); - m_keywordsScorer.ForEachLanguage( - [&](int8_t lang) { params.GetLangs().Insert(static_cast(lang)); }); + m_keywordsScorer.ForEachLanguage([¶ms](int8_t lang) + { + params.GetLangs().Insert(static_cast(lang)); + }); } void Processor::InitGeocoder(Geocoder::Params & geocoderParams, SearchParams const & searchParams) diff --git a/search/processor.hpp b/search/processor.hpp index 564bfbc314..5c6cea03ab 100644 --- a/search/processor.hpp +++ b/search/processor.hpp @@ -70,7 +70,7 @@ public: void SetViewport(m2::RectD const & viewport); void SetPreferredLocale(std::string const & locale); void SetInputLocale(std::string const & locale); - void SetQuery(std::string const & query); + void SetQuery(std::string const & query, bool categorialRequest = false); inline std::string const & GetPivotRegion() const { return m_region; } inline bool IsEmptyQuery() const { return m_prefix.empty() && m_tokens.empty(); } diff --git a/search/search_integration_tests/processor_test.cpp b/search/search_integration_tests/processor_test.cpp index c9f6c0678d..45ce3bbbee 100644 --- a/search/search_integration_tests/processor_test.cpp +++ b/search/search_integration_tests/processor_test.cpp @@ -848,8 +848,8 @@ UNIT_CLASS_TEST(ProcessorTest, TestCategorialSearch) { Rules const rules = {ExactMatch(wonderlandId, hotel1), ExactMatch(wonderlandId, hotel2)}; - TEST(ResultsMatch("hotel ", rules), ()); - TEST(ResultsMatch("hôTeL ", rules), ()); + TEST(CategoryMatch("hotel ", rules), ()); + TEST(CategoryMatch("hôTeL ", rules), ()); } { @@ -858,14 +858,13 @@ UNIT_CLASS_TEST(ProcessorTest, TestCategorialSearch) // A category with a rare name. The word "Entertainment" // occurs exactly once in the list of categories and starts // with a capital letter. This is a test for normalization. - TEST(ResultsMatch("entertainment ", rules), ()); + TEST(CategoryMatch("entertainment ", rules), ()); } { Rules const rules = {ExactMatch(wonderlandId, hotel1), ExactMatch(wonderlandId, hotel2)}; - auto request = MakeRequest("гостиница ", "ru"); - TEST(ResultsMatch(request->Results(), rules), ()); + TEST(CategoryMatch("гостиница ", rules, "ru"), ()); } { @@ -874,8 +873,8 @@ UNIT_CLASS_TEST(ProcessorTest, TestCategorialSearch) // Hotel unicode character: both a synonym and and emoji. uint32_t const hotelEmojiCodepoint = 0x0001F3E8; strings::UniString const hotelUniString(1, hotelEmojiCodepoint); - auto request = MakeRequest(ToUtf8(hotelUniString)); - TEST(ResultsMatch(request->Results(), rules), ()); + + TEST(CategoryMatch(ToUtf8(hotelUniString), rules), ()); } { @@ -3027,4 +3026,31 @@ UNIT_CLASS_TEST(ProcessorTest, TestRankingInfo_MultipleOldNames) checkResult("Ленинград", "Санкт-Петербург (Ленинград)"); checkResult("Петроград", "Санкт-Петербург (Петроград)"); } + +/// @todo We are not ready for this test yet. +/* +UNIT_CLASS_TEST(ProcessorTest, BurgerStreet) +{ + string const countryName = "Wonderland"; + + TestPOI burger({1.0, 1.0}, "Dummy", "en"); + burger.SetTypes({{"amenity", "fast_food"}, {"cuisine", "burger"}}); + + TestStreet street({{2.0, 2.0}, {3.0, 3.0}}, "Burger street", "en"); + street.SetHighwayType("residential"); + + auto countryId = BuildCountry(countryName, [&](TestMwmBuilder & builder) + { + builder.Add(burger); + builder.Add(street); + }); + + SetViewport(m2::RectD(0, 0, 3, 3)); + + { + Rules rules{ExactMatch(countryId, burger), ExactMatch(countryId, street)}; + TEST(ResultsMatch("burger", rules), ()); + } +} +*/ } // namespace processor_test diff --git a/search/search_integration_tests/smoke_test.cpp b/search/search_integration_tests/smoke_test.cpp index cf187cde85..49d55c2247 100644 --- a/search/search_integration_tests/smoke_test.cpp +++ b/search/search_integration_tests/smoke_test.cpp @@ -68,8 +68,6 @@ public: { SetTypes({{"shop", "alcohol"}}); } - - ~AlcoShop() override = default; }; class SubwayStation : public TestPOI @@ -80,8 +78,6 @@ public: { SetTypes({{"railway", "station", "subway"}}); } - - ~SubwayStation() override = default; }; class SubwayStationMoscow : public TestPOI @@ -92,8 +88,6 @@ public: { SetTypes({{"railway", "station", "subway", "moscow"}}); } - - ~SubwayStationMoscow() override = default; }; UNIT_CLASS_TEST(SmokeTest, Smoke) @@ -105,7 +99,8 @@ UNIT_CLASS_TEST(SmokeTest, Smoke) AlcoShop brandyShop(m2::PointD(0, 1), "Brandy shop", "en"); AlcoShop vodkaShop(m2::PointD(1, 1), "Russian vodka shop", "en"); - auto id = BuildMwm(kCountryName, DataHeader::MapType::Country, [&](TestMwmBuilder & builder) { + auto id = BuildMwm(kCountryName, DataHeader::MapType::Country, [&](TestMwmBuilder & builder) + { builder.Add(wineShop); builder.Add(tequilaShop); builder.Add(brandyShop); @@ -117,18 +112,16 @@ UNIT_CLASS_TEST(SmokeTest, Smoke) SetViewport(m2::RectD(m2::PointD(0, 0), m2::PointD(100, 100))); { - Rules rules = {ExactMatch(id, tequilaShop)}; - /// @todo Passing "wine" will interpret request as "categorial" only - /// (see IsCategorialRequest() after adding craft-winery category). - /// Should avoid this strange logic in search core and pass "categorial" request flag via input SearchParams. - TEST(ResultsMatch("tequila ", rules), ()); + Rules rules = {ExactMatch(id, wineShop)}; + TEST(ResultsMatch("wine ", rules), ()); } - { - Rules rules = {ExactMatch(id, wineShop), ExactMatch(id, tequilaShop), - ExactMatch(id, brandyShop), ExactMatch(id, vodkaShop)}; - TEST(ResultsMatch("shop ", rules), ()); - } + Rules const allRule = { ExactMatch(id, wineShop), ExactMatch(id, tequilaShop), + ExactMatch(id, brandyShop), ExactMatch(id, vodkaShop) }; + + TEST(ResultsMatch("shop ", allRule), ()); + TEST(ResultsMatch("alcohol ", allRule), ()); + TEST(CategoryMatch("алкоголь", allRule, "ru"), ()); } UNIT_CLASS_TEST(SmokeTest, DeepCategoryTest) @@ -232,6 +225,7 @@ UNIT_CLASS_TEST(SmokeTest, CategoriesTest) notSupportedTypes.insert(cl.GetTypeByPath(tags)); auto const & holder = GetDefaultCategories(); + auto testCategory = [&](uint32_t type, CategoriesHolder::Category const &) { if (invisibleTypes.find(type) != invisibleTypes.end()) @@ -250,11 +244,10 @@ UNIT_CLASS_TEST(SmokeTest, CategoriesTest) auto id = BuildMwm(countryName, DataHeader::MapType::Country, [&](TestMwmBuilder & builder) { builder.AddSafe(poi); }); - SetViewport(m2::RectD(m2::PointD(0.0, 0.0), m2::PointD(2.0, 2.0))); { Rules rules = {ExactMatch(id, poi)}; - auto const query = holder.GetReadableFeatureType(type, CategoriesHolder::kEnglishCode) + " "; - TEST(ResultsMatch(query, categoryIsSearchable ? rules : Rules{}), (query)); + auto const query = holder.GetReadableFeatureType(type, CategoriesHolder::kEnglishCode); + TEST(CategoryMatch(query, categoryIsSearchable ? rules : Rules{}), (query)); } DeregisterMap(countryName); }; diff --git a/search/search_params.hpp b/search/search_params.hpp index 63323c21bb..ed4cfc9560 100644 --- a/search/search_params.hpp +++ b/search/search_params.hpp @@ -30,7 +30,7 @@ struct SearchParams static double constexpr kDefaultVillageSearchRadiusM = 2e5; using TimeDurationT = base::Timer::DurationT; - /// @todo Short timeouts leads to a non-working search on slow devices. Design a better solution. + /// @todo Short timeouts lead to a non-working search on slow devices. Design a better solution. static TimeDurationT constexpr kDefaultTimeout = std::chrono::seconds(8); static TimeDurationT constexpr kDefaultDesktopTimeout = std::chrono::seconds(8); @@ -92,6 +92,9 @@ struct SearchParams // Needed to highlight matching parts of search result names. bool m_needHighlighting = false; + /// True if you need *pure* category results, without names/addresses/etc matching. + bool m_categorialRequest = false; + bookmarks::GroupId m_bookmarksGroupId = bookmarks::kInvalidGroupId; // Amount of time after which the search is aborted. diff --git a/search/search_tests_support/helpers.cpp b/search/search_tests_support/helpers.cpp index d5954f3354..8f5560b5b0 100644 --- a/search/search_tests_support/helpers.cpp +++ b/search/search_tests_support/helpers.cpp @@ -10,6 +10,7 @@ namespace search { using namespace std; +using namespace tests_support; SearchTest::SearchTest() : m_scopedLog(LDEBUG) @@ -25,24 +26,30 @@ void SearchTest::RegisterCountry(string const & name, m2::RectD const & rect) infoGetter.AddCountry(storage::CountryDef(name, rect)); } -bool SearchTest::ResultsMatch(string const & query, - vector> const & rules) +bool SearchTest::ResultsMatch(string const & query, Rules const & rules) { return ResultsMatch(query, "en" /* locale */, rules); } -bool SearchTest::ResultsMatch(string const & query, string const & locale, - vector> const & rules) +bool SearchTest::CategoryMatch(std::string const & query, Rules const & rules, string const & locale) { - tests_support::TestSearchRequest request(m_engine, query, locale, Mode::Everywhere, m_viewport); + TestSearchRequest request(m_engine, query, locale, Mode::Everywhere, m_viewport); + request.SetCategorial(); + request.Run(); return MatchResults(m_dataSource, rules, request.Results()); } -bool SearchTest::ResultsMatch(string const & query, Mode mode, - vector> const & rules) +bool SearchTest::ResultsMatch(string const & query, string const & locale, Rules const & rules) { - tests_support::TestSearchRequest request(m_engine, query, "en", mode, m_viewport); + TestSearchRequest request(m_engine, query, locale, Mode::Everywhere, m_viewport); + request.Run(); + return MatchResults(m_dataSource, rules, request.Results()); +} + +bool SearchTest::ResultsMatch(string const & query, Mode mode, Rules const & rules) +{ + TestSearchRequest request(m_engine, query, "en", mode, m_viewport); request.Run(); return MatchResults(m_dataSource, rules, request.Results()); } @@ -54,7 +61,7 @@ bool SearchTest::ResultsMatch(vector const & results, Rules cons bool SearchTest::ResultsMatch(SearchParams const & params, Rules const & rules) { - tests_support::TestSearchRequest request(m_engine, params); + TestSearchRequest request(m_engine, params); request.Run(); return ResultsMatch(request.Results(), rules); } @@ -66,26 +73,26 @@ bool SearchTest::ResultMatches(search::Result const & result, Rule const & rule) bool SearchTest::AlternativeMatch(string const & query, vector const & rulesList) { - tests_support::TestSearchRequest request(m_engine, query, "en", Mode::Everywhere, m_viewport); + TestSearchRequest request(m_engine, query, "en", Mode::Everywhere, m_viewport); request.Run(); return tests_support::AlternativeMatch(m_dataSource, rulesList, request.Results()); } size_t SearchTest::GetResultsNumber(string const & query, string const & locale) { - tests_support::TestSearchRequest request(m_engine, query, locale, Mode::Everywhere, m_viewport); + TestSearchRequest request(m_engine, query, locale, Mode::Everywhere, m_viewport); request.Run(); return request.Results().size(); } -unique_ptr SearchTest::MakeRequest(SearchParams params) +unique_ptr SearchTest::MakeRequest(SearchParams params) { - auto request = make_unique(m_engine, params); + auto request = make_unique(m_engine, params); request->Run(); return request; } -unique_ptr SearchTest::MakeRequest( +unique_ptr SearchTest::MakeRequest( string const & query, string const & locale /* = "en" */) { SearchParams params; @@ -95,11 +102,10 @@ unique_ptr SearchTest::MakeRequest( params.m_mode = Mode::Everywhere; params.m_needAddress = true; params.m_suggestsEnabled = false; - params.m_streetSearchRadiusM = tests_support::TestSearchRequest::kDefaultTestStreetSearchRadiusM; - params.m_villageSearchRadiusM = - tests_support::TestSearchRequest::kDefaultTestVillageSearchRadiusM; + params.m_streetSearchRadiusM = TestSearchRequest::kDefaultTestStreetSearchRadiusM; + params.m_villageSearchRadiusM = TestSearchRequest::kDefaultTestVillageSearchRadiusM; - auto request = make_unique(m_engine, params); + auto request = make_unique(m_engine, params); request->Run(); return request; } diff --git a/search/search_tests_support/helpers.hpp b/search/search_tests_support/helpers.hpp index 81754f2631..1dccfccbe7 100644 --- a/search/search_tests_support/helpers.hpp +++ b/search/search_tests_support/helpers.hpp @@ -32,6 +32,7 @@ public: inline void SetViewport(m2::RectD const & viewport) { m_viewport = viewport; } bool ResultsMatch(std::string const & query, Rules const & rules); + bool CategoryMatch(std::string const & query, Rules const & rules, std::string const & locale = "en"); bool ResultsMatch(std::string const & query, std::string const & locale, Rules const & rules); diff --git a/search/search_tests_support/test_search_engine.cpp b/search/search_tests_support/test_search_engine.cpp index 616a655534..f993d88144 100644 --- a/search/search_tests_support/test_search_engine.cpp +++ b/search/search_tests_support/test_search_engine.cpp @@ -26,7 +26,7 @@ TestSearchEngine::TestSearchEngine(DataSource & dataSource, Engine::Params const { } -weak_ptr<::search::ProcessorHandle> TestSearchEngine::Search(::search::SearchParams const & params) +weak_ptr TestSearchEngine::Search(SearchParams const & params) { return m_engine.Search(params); } diff --git a/search/search_tests_support/test_search_engine.hpp b/search/search_tests_support/test_search_engine.hpp index 8fbfb3e05d..aa76886c24 100644 --- a/search/search_tests_support/test_search_engine.hpp +++ b/search/search_tests_support/test_search_engine.hpp @@ -26,13 +26,13 @@ public: void LoadCitiesBoundaries() { m_engine.LoadCitiesBoundaries(); } - std::weak_ptr Search(search::SearchParams const & params); + std::weak_ptr Search(SearchParams const & params); storage::CountryInfoGetter & GetCountryInfoGetter() { return *m_infoGetter; } private: std::unique_ptr m_infoGetter; - search::Engine m_engine; + Engine m_engine; }; } // namespace tests_support } // namespace search diff --git a/search/search_tests_support/test_search_request.hpp b/search/search_tests_support/test_search_request.hpp index 5c5dca41a7..bf0f9ceb47 100644 --- a/search/search_tests_support/test_search_request.hpp +++ b/search/search_tests_support/test_search_request.hpp @@ -32,6 +32,8 @@ public: std::string const & locale, Mode mode, m2::RectD const & viewport); TestSearchRequest(TestSearchEngine & engine, SearchParams const & params); + void SetCategorial() { m_params.m_categorialRequest = true; } + // Initiates the search and waits for it to finish. void Run(); -- 2.45.3 From 4de66fce16529b49d3a7c1a4f55821d7f5bbf102 Mon Sep 17 00:00:00 2001 From: inkhorn <103287762+inkh0rn@users.noreply.github.com> Date: Mon, 13 Jun 2022 18:47:07 -0300 Subject: [PATCH 3/4] update and new strings for ptbr (#2731) * small update for ptbr Signed-off-by: inkh0rn <103287762+inkh0rn@users.noreply.github.com> * new strings added for ptbr Signed-off-by: inkh0rn <103287762+inkh0rn@users.noreply.github.com> --- data/categories.txt | 3 +- data/strings/types_strings.txt | 61 +++++++++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/data/categories.txt b/data/categories.txt index ffff29efa2..ad7106561c 100644 --- a/data/categories.txt +++ b/data/categories.txt @@ -3224,6 +3224,7 @@ ja:自転車修理 ko:자전거 수리 nl:4Fiets reparatie pt:Conserto de bicicleta|4bicicleta +pt-BR:Oficina de bicicletas|4bicicleta ru:4Велоремонт|велосипед|ремонт|ремонт велосипедов tr:4Bisiklet tamiri|4Bisiklet uk:4Велоремонт|велосипед|ремонт|ремонт велосипедів @@ -4783,7 +4784,7 @@ it:WC ja:お手洗い|便所|厠 pl:wc|ubikacja|wychodek pt:Casa de banho|WC|Sanitários|lavatório -pt-BR:toalete|wc|sanitários +pt-BR:toalete|wc|sanitários|lavabo|privada es:3Baños|aseos|3lavabo|inodoro|baño|wc eu:3Komunak|3konketa sv:WC|toalett diff --git a/data/strings/types_strings.txt b/data/strings/types_strings.txt index a43a65e5b1..ed3d57d1c9 100644 --- a/data/strings/types_strings.txt +++ b/data/strings/types_strings.txt @@ -637,7 +637,7 @@ nl = Fiets reparatie station pl = Stacja naprawy rowerów pt = Estação de conserto de bicicletas - pt-BR = Estação de conserto de bicicletas + pt-BR = Oficina de conserto de bicicletas ru = Станция ремонта велосипедов sw = Cykelreparationsstation th = สถานีซ่อมจักรยาน @@ -8295,15 +8295,18 @@ ref = type.highway.road.bridge ja = 馬道(橋) pl = Most drogowy dla koni + pt-BR = Ponte para cavaleiros [type.highway.bridleway.permissive] ref = type.highway.bridleway pl = Droga dla koni ograniczona + pt-BR = Caminho para cavaleiros permissivo [type.highway.bridleway.tunnel] ref = type.highway.road.tunnel ja = 馬道(トンネル) pl = Tunel drogowy dla koni + pt-BR = Túnel para cavaleiros [type.highway.bus_stop] en = Bus Stop @@ -8395,15 +8398,18 @@ ref = type.highway.road.bridge ja = 自転車道(橋) pl = Most drogowy dla rowerów + pt-BR = Ponte para ciclistas [type.highway.cycleway.permissive] ref = type.highway.cycleway pl = Droga rowerowa ograniczona + pt-BR = Caminho para ciclistas permissivo [type.highway.cycleway.tunnel] ref = type.highway.road.tunnel ja = 自転車道(トンネル) pl = Tunel drogowy dla rowerów + pt-BR = Túnel para ciclistas [type.highway.elevator] en = Elevator @@ -8413,6 +8419,7 @@ eu = Igogailua fr = Ascenseur mr = उद्वाहक(लिफ्ट) + pt-BR = Elevador ru = Лифт tr = Asansör uk = Ліфт @@ -8464,6 +8471,7 @@ en = Pedestrian Zone mr = पादचारी झोन pl = Obszar chodnika + pt-BR = Área de pedestres ru = Пешеходная зона tr = Yaya Alanı uk = Пішохідна зона @@ -8473,27 +8481,28 @@ en = Pedestrian Bridge mr = पादचारी पूल pl = Most dla pieszych + pt-BR = Ponte para pedestres ru = Пешеходный мост [type.highway.footway.demanding_alpine_hiking] ref = type.highway.path de = Anspruchsvoller Alpinwanderweg pt = Caminho pedonal - pt-BR = Caminho pedonal + pt-BR = Caminho pedonal difícil zh-Hans = 步行道路 [type.highway.footway.demanding_mountain_hiking] ref = type.highway.path de = Anspruchsvoller Bergwanderweg pt = Caminho pedonal - pt-BR = Caminho pedonal + pt-BR = Caminho pedonal difícil zh-Hans = 步行道路 [type.highway.footway.difficult_alpine_hiking] ref = type.highway.path de = Schwieriger Alpinwanderweg pt = Caminho pedonal - pt-BR = Caminho pedonal + pt-BR = Caminho pedonal difícil zh-Hans = 步行道路 [type.highway.footway.hiking] @@ -8519,6 +8528,7 @@ de = Fußgängertunnel mr = पादचारी बोगदा pl = Tunel dla pieszych + pt-BR = Túnel para pedestres ru = Пешеходный тоннель [type.highway.ford] @@ -8596,6 +8606,7 @@ ref = type.highway.road.tunnel de = Spielstraßentunnel pl = Tunel ulicy + pt-BR = Túnel rodoviário [type.highway.motorway] en = Motorway @@ -8637,6 +8648,7 @@ de = Autobahnbrücke mr = महामार्ग पूल pl = Most drogowy + pt-BR = Ponte rodoviária ru = Автомобильный мост [type.highway.motorway.tunnel] @@ -8645,6 +8657,7 @@ de = Autobahntunnel mr = महामार्ग बोगदा pl = Tunel drogowy + pt-BR = Túnel rodoviário ru = Автомобильный тоннель [type.highway.motorway_junction] @@ -8685,6 +8698,7 @@ de = Autobahnauffahrt mr = महामार्ग उतार pt = Ligação a autoestrada + pt-BR = Acesso a rodovia ru = Съезд с автомагистрали tr = Otoyol Rampası uk = З'їзд з автомагістралі @@ -8735,6 +8749,7 @@ ref = type.highway.path de = Alpinwanderweg pl = Ścieżka wspinaczkowa + pt-BR = Caminho pedonal [type.highway.path.bicycle] ref = type.highway.path @@ -8742,6 +8757,7 @@ de = Radweg mr = सायकल आणि पाऊलवाट pl = Ścieżka dla rowerów + pt-BR = Caminho para ciclistas ru = Велопешеходная дорожка tr = Bisiklet ve Yürüyüş Yolu uk = Велопішохідна доріжка @@ -8752,23 +8768,28 @@ [type.highway.path.demanding_alpine_hiking] ref = type.highway.path de = Anspruchsvoller Alpinwanderweg + pt-BR = Caminho pedonal difícil [type.highway.path.demanding_mountain_hiking] ref = type.highway.path de = Anspruchsvoller Bergwanderweg + pt-BR = Caminho pedonal difícil [type.highway.path.difficult_alpine_hiking] ref = type.highway.path de = Schwieriger Alpinwanderweg + pt-BR = Caminho pedonal difícil [type.highway.path.hiking] ref = type.highway.path de = Wanderweg + pt-BR = Caminho pedonal [type.highway.path.horse] ref = type.highway.path en = Bridle Path de = Reitweg + pt-BR = Caminho para cavaleiros ru = Конная тропа tr = Atlı Yolu uk = Кінна стежка @@ -8776,6 +8797,7 @@ [type.highway.path.mountain_hiking] ref = type.highway.path de = Bergwanderweg + pt-BR = Caminho pedonal [type.highway.path.permissive] ref = type.highway.path @@ -8831,6 +8853,7 @@ en = Pedestrian Bridge mr = पादचारी पूल pl = Most pasażu pieszego + pt-BR = Ponte para pedestres ru = Пешеходный мост [type.highway.pedestrian.tunnel] @@ -8839,6 +8862,7 @@ de = Fußgängertunnel mr = पादचारी बोगदा pl = Tunel pasażu pieszego + pt-BR = Túnel para pedestres ru = Пешеходный тоннель [type.highway.primary] @@ -8882,11 +8906,13 @@ ref = type.highway.road.tunnel de = Hauptstraßentunnel pl = Tunel drogowy + pt-BR = Túnel rodoviário [type.highway.primary_link] ref = type.highway.primary en = Primary Road Ramp mr = प्राथमिक मार्ग उतार + pt-BR = Saída de rodovia ru = Съезд с шоссе uk = З'їзд з шосе zh-Hans = 主要道路连接路 @@ -8975,6 +9001,7 @@ [type.highway.residential.tunnel] ref = type.highway.road.tunnel de = Wohnstraßentunnel + pt-BR = Túnel residencial [type.highway.rest_area] en = Rest Area @@ -9164,11 +9191,13 @@ ref = type.highway.road.tunnel de = Straßentunnel pl = Tunel drogowy + pt-BR = Túnel rodoviário [type.highway.secondary_link] ref = type.highway.secondary en = Secondary Road Ramp mr = दुय्यम मार्ग उतार + pt-BR = Saída de rodovia ru = Съезд с автодороги uk = З'їзд з автодороги zh-Hans = 次要道路连接路 @@ -9181,6 +9210,7 @@ ref = type.highway.road.tunnel de = Straßentunnel pl = Tunel drogowy + pt-BR = Túnel rodoviário [type.highway.service] en = Service Road @@ -9232,6 +9262,7 @@ uk = Під'їзд zh-Hans = 车道 zh-Hant = 車道 + pt-BR = Via de acesso [type.highway.service.parking_aisle] ref = type.highway.service @@ -9251,6 +9282,7 @@ ref = type.highway.road.tunnel de = Straßentunnel pl = Tunel drogowy + pt-BR = Túnel rodoviário [type.highway.services] en = Service Area @@ -9382,11 +9414,13 @@ ref = type.highway.road.tunnel de = Straßentunnel pl = Tunel drogowy + pt-BR = Túnel rodoviário [type.highway.tertiary_link] ref = type.highway.tertiary en = Tertiary Road Ramp mr = तृतीयक मार्ग उतार + pt-BR = Saída de rodovia ru = Съезд с дороги tr = Üçüncül Yol Rampası uk = З'їзд з дороги @@ -9400,6 +9434,7 @@ ref = type.highway.road.tunnel de = Straßentunnel pl = Tunel drogowy + pt-BR = Túnel rodoviário [type.highway.track] en = Track @@ -9466,6 +9501,7 @@ ref = type.highway.road.tunnel de = Straßentunnel pl = Tunel drogowy + pt-BR = Túnel rodoviário [type.highway.traffic_signals] en = Traffic Lights @@ -9541,11 +9577,13 @@ [type.highway.trunk.tunnel] ref = type.highway.road.tunnel de = Schnellstraßentunnel + pt-BR = Túnel rodoviário [type.highway.trunk_link] ref = type.highway.trunk en = National Highway Ramp mr = राष्ट्रीय महामार्ग उतार + pt-BR = Saída de rodovia ru = Съезд с трассы uk = З'їзд з траси zh-Hans = 干线道路连接路 @@ -9557,6 +9595,7 @@ [type.highway.trunk_link.tunnel] ref = type.highway.road.tunnel de = Straßentunnel + pt-BR = Túnel rodoviário [type.highway.unclassified] en = Minor Road @@ -9602,23 +9641,28 @@ ref = type.highway.road.tunnel de = Straßentunnel pl = Tunel drogowy + pt-BR = Túnel rodoviário [type.area_highway.cycleway] ref = type.highway.cycleway fi = Pyörätie + pt-BR = Caminho para ciclistas [type.area_highway.footway] ref = type.highway.footway de = Weg fi = Kävelytie + pt-BR = Caminho pedonal [type.area_highway.living_street] ref = type.highway.living_street de = Wohnstraße + pt-BR = Rua residencial [type.area_highway.motorway] ref = type.highway.motorway fi = Moottoritie + pt-BR = Rodovia [type.area_highway.path] ref = type.highway.path @@ -9626,6 +9670,7 @@ [type.area_highway.pedestrian] ref = type.highway.pedestrian fi = Kävelykatu + pt-BR = Caminho pedonal [type.area_highway.primary] ref = type.highway.primary @@ -9645,6 +9690,7 @@ [type.area_highway.steps] ref = type.highway.steps fi = Portaat + pt-BR = Travessia [type.area_highway.track] ref = type.highway.track @@ -10341,10 +10387,12 @@ comment = In most (European) countries, сemeteries are usually independent of places of worship (e.g. military cemeteries), while grave yards are usually the yard of a place of worship. ref = type.amenity.grave_yard en = Cemetery + pt-BR = Cemitério [type.landuse.cemetery.christian] ref = type.amenity.grave_yard.christian en = Christian Cemetery + pt-BR = Cemitério cristão [type.landuse.churchyard] en = Churchyard @@ -10416,6 +10464,7 @@ fr = Établissements éducatifs it = Strutture didattiche mr = शैक्षणिक सुविधा + pt-BR = Instituições educacionais ru = Образовательные учреждения uk = Освітній заклади @@ -12016,6 +12065,7 @@ be = Сілас de = Silo mr = पेव(धान्यसाठा) + pt-BR = Silo ru = Элеватор tr = Ambar uk = Силос @@ -12026,6 +12076,7 @@ de = Speichertank fr = Réservoir mr = साठवण टाकी + pt-BR = Tanque de armazenagem ru = Резервуар tr = Depolama Tankı uk = Резервуар @@ -12791,6 +12842,7 @@ nl = Reservoir pl = Zbiornik pt = Reservatório + pt-BR = Reservatório ro = Rezervor ru = Водохранилище sk = Nádrž @@ -14610,6 +14662,7 @@ de = Haltepunkt pl = Przystanek kolejowy pt = Apeadeiro + pt-BR = Ponto de parada zh-Hans = 小站 zh-Hant = 小站 -- 2.45.3 From 9fd923c17e62f1d2fea5b5968b23ff2a4eb5d9d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20G=C3=A9czi?= Date: Tue, 14 Jun 2022 00:00:00 +0000 Subject: [PATCH 4/4] [platform][linux] Use $XDG_DATA_HOME if defined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ferenc Géczi --- platform/platform_linux.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/platform/platform_linux.cpp b/platform/platform_linux.cpp index 7912a75b7d..506d847029 100644 --- a/platform/platform_linux.cpp +++ b/platform/platform_linux.cpp @@ -107,6 +107,7 @@ Platform::Platform() JoinPath(*execDir, "..", "organicmaps", "data"), // build-omim-{debug,release} JoinPath(*execDir, "..", "share"), // installed version with packages JoinPath(*execDir, "..", "OMaps"), // installed version without packages + JoinPath(*execDir, "..", "share", "organicmaps", "data"), // flatpak-build }; for (auto const & dir : dirsToScan) { @@ -122,7 +123,12 @@ Platform::Platform() // Use ~/.local/share/OMaps if resources directory was not writable. if (!m_resourcesDir.empty() && m_writableDir.empty()) { - m_writableDir = JoinPath(*homeDir, ".local", "share", "OMaps"); + // The writableLocation does the same for AppDataLocation, AppLocalDataLocation, + // and GenericDataLocation. Provided, that test mode is not enabled, then + // first it checks ${XDG_DATA_HOME}, if empty then it falls back to ${HOME}/.local/share + m_writableDir = JoinPath(QStandardPaths::writableLocation( + QStandardPaths::AppDataLocation).toStdString(), "OMaps"); + if (!MkDirRecursively(m_writableDir)) MYTHROW(FileSystemException, ("Can't create writable directory:", m_writableDir)); } -- 2.45.3