Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drape/overlay_tree.hpp6
-rw-r--r--drape_frontend/CMakeLists.txt2
-rw-r--r--drape_frontend/backend_renderer.cpp10
-rw-r--r--drape_frontend/backend_renderer.hpp2
-rw-r--r--drape_frontend/drape_engine.cpp7
-rw-r--r--drape_frontend/drape_engine.hpp6
-rwxr-xr-xdrape_frontend/drape_frontend.pro2
-rwxr-xr-xdrape_frontend/frontend_renderer.cpp42
-rwxr-xr-xdrape_frontend/frontend_renderer.hpp11
-rw-r--r--drape_frontend/message.hpp1
-rw-r--r--drape_frontend/message_subclasses.hpp17
-rw-r--r--drape_frontend/overlays_tracker.cpp74
-rw-r--r--drape_frontend/overlays_tracker.hpp65
-rw-r--r--map/framework.cpp9
-rw-r--r--platform/platform_mac.mm6
15 files changed, 244 insertions, 16 deletions
diff --git a/drape/overlay_tree.hpp b/drape/overlay_tree.hpp
index 9f9818a3f1..e40bd7e419 100644
--- a/drape/overlay_tree.hpp
+++ b/drape/overlay_tree.hpp
@@ -47,6 +47,8 @@ class OverlayTree : public m4::Tree<ref_ptr<OverlayHandle>, detail::OverlayTrait
using TBase = m4::Tree<ref_ptr<OverlayHandle>, detail::OverlayTraits>;
public:
+ using HandlesCache = unordered_set<ref_ptr<OverlayHandle>, detail::OverlayHasher>;
+
OverlayTree();
bool Frame();
@@ -57,6 +59,8 @@ public:
void Remove(ref_ptr<OverlayHandle> handle);
void EndOverlayPlacing();
+ HandlesCache const & GetHandlesCache() const { return m_handlesCache; }
+
void Select(m2::RectD const & rect, TOverlayContainer & result) const;
void Select(m2::PointD const & glbPoint, TOverlayContainer & result) const;
@@ -94,7 +98,7 @@ private:
int m_frameCounter;
array<vector<ref_ptr<OverlayHandle>>, dp::OverlayRanksCount> m_handles;
- unordered_set<ref_ptr<OverlayHandle>, detail::OverlayHasher> m_handlesCache;
+ HandlesCache m_handlesCache;
bool m_followingMode;
bool m_isDisplacementEnabled;
diff --git a/drape_frontend/CMakeLists.txt b/drape_frontend/CMakeLists.txt
index 693dbe216f..0094079022 100644
--- a/drape_frontend/CMakeLists.txt
+++ b/drape_frontend/CMakeLists.txt
@@ -129,6 +129,8 @@ set(
navigator.hpp
overlay_batcher.cpp
overlay_batcher.hpp
+ overlays_tracker.cpp
+ overlays_tracker.hpp
path_symbol_shape.cpp
path_symbol_shape.hpp
path_text_shape.cpp
diff --git a/drape_frontend/backend_renderer.cpp b/drape_frontend/backend_renderer.cpp
index 253b28fd6b..51d8415a1c 100644
--- a/drape_frontend/backend_renderer.cpp
+++ b/drape_frontend/backend_renderer.cpp
@@ -25,7 +25,7 @@
namespace df
{
-BackendRenderer::BackendRenderer(Params const & params)
+BackendRenderer::BackendRenderer(Params && params)
: BaseRenderer(ThreadsCommutator::ResourceUploadThread, params)
, m_model(params.m_model)
, m_readManager(make_unique_dp<ReadManager>(params.m_commutator, m_model,
@@ -431,9 +431,13 @@ void BackendRenderer::AcceptMessage(ref_ptr<Message> message)
case Message::SetCustomSymbols:
{
ref_ptr<SetCustomSymbolsMessage> msg = message;
- m_readManager->UpdateCustomSymbols(msg->AcceptSymbols());
+ CustomSymbols customSymbols = msg->AcceptSymbols();
+ std::vector<FeatureID> features;
+ for (auto const & symbol : customSymbols)
+ features.push_back(symbol.first);
+ m_readManager->UpdateCustomSymbols(std::move(customSymbols));
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
- make_unique_dp<SetCustomSymbolsMessage>(CustomSymbols()),
+ make_unique_dp<UpdateCustomSymbolsMessage>(std::move(features)),
MessagePriority::Normal);
break;
}
diff --git a/drape_frontend/backend_renderer.hpp b/drape_frontend/backend_renderer.hpp
index b8b1f8a417..6365c263eb 100644
--- a/drape_frontend/backend_renderer.hpp
+++ b/drape_frontend/backend_renderer.hpp
@@ -57,7 +57,7 @@ public:
bool m_simplifiedTrafficColors;
};
- BackendRenderer(Params const & params);
+ BackendRenderer(Params && params);
~BackendRenderer() override;
void Teardown();
diff --git a/drape_frontend/drape_engine.cpp b/drape_frontend/drape_engine.cpp
index 2f5e4d6e90..c4d8dbd32e 100644
--- a/drape_frontend/drape_engine.cpp
+++ b/drape_frontend/drape_engine.cpp
@@ -49,19 +49,20 @@ DrapeEngine::DrapeEngine(Params && params)
bind(&DrapeEngine::TapEvent, this, _1),
bind(&DrapeEngine::UserPositionChanged, this, _1),
bind(&DrapeEngine::MyPositionModeChanged, this, _1, _2),
- mode, make_ref(m_requestedTiles), timeInBackground,
+ mode, make_ref(m_requestedTiles),
+ move(params.m_showEventCallback), timeInBackground,
params.m_allow3dBuildings, params.m_trafficEnabled,
params.m_blockTapEvents, params.m_isFirstLaunch,
params.m_isRoutingActive, params.m_isAutozoomEnabled);
- m_frontend = make_unique_dp<FrontendRenderer>(frParams);
+ m_frontend = make_unique_dp<FrontendRenderer>(move(frParams));
BackendRenderer::Params brParams(frParams.m_commutator, frParams.m_oglContextFactory,
frParams.m_texMng, params.m_model,
params.m_model.UpdateCurrentCountryFn(),
make_ref(m_requestedTiles), params.m_allow3dBuildings,
params.m_trafficEnabled, params.m_simplifiedTrafficColors);
- m_backend = make_unique_dp<BackendRenderer>(brParams);
+ m_backend = make_unique_dp<BackendRenderer>(move(brParams));
m_widgetsInfo = move(params.m_info);
diff --git a/drape_frontend/drape_engine.hpp b/drape_frontend/drape_engine.hpp
index 28eb82c943..abcb70c001 100644
--- a/drape_frontend/drape_engine.hpp
+++ b/drape_frontend/drape_engine.hpp
@@ -5,6 +5,7 @@
#include "drape_frontend/custom_symbol.hpp"
#include "drape_frontend/frontend_renderer.hpp"
#include "drape_frontend/route_shape.hpp"
+#include "drape_frontend/overlays_tracker.hpp"
#include "drape_frontend/scenario_manager.hpp"
#include "drape_frontend/selection_shape.hpp"
#include "drape_frontend/threads_commutator.hpp"
@@ -56,7 +57,8 @@ public:
bool firstLaunch,
bool isRoutingActive,
bool isAutozoomEnabled,
- bool simplifiedTrafficColors)
+ bool simplifiedTrafficColors,
+ OverlayShowEventCallback && showEventCallback)
: m_factory(factory)
, m_stringsBundle(stringBundle)
, m_viewport(viewport)
@@ -75,6 +77,7 @@ public:
, m_isRoutingActive(isRoutingActive)
, m_isAutozoomEnabled(isAutozoomEnabled)
, m_simplifiedTrafficColors(simplifiedTrafficColors)
+ , m_showEventCallback(move(showEventCallback))
{}
ref_ptr<dp::OGLContextFactory> m_factory;
@@ -95,6 +98,7 @@ public:
bool m_isRoutingActive;
bool m_isAutozoomEnabled;
bool m_simplifiedTrafficColors;
+ OverlayShowEventCallback m_showEventCallback;
};
DrapeEngine(Params && params);
diff --git a/drape_frontend/drape_frontend.pro b/drape_frontend/drape_frontend.pro
index 15c1b4572b..9010a1bbe8 100755
--- a/drape_frontend/drape_frontend.pro
+++ b/drape_frontend/drape_frontend.pro
@@ -64,6 +64,7 @@ SOURCES += \
my_position_controller.cpp \
navigator.cpp \
overlay_batcher.cpp \
+ overlays_tracker.cpp \
path_symbol_shape.cpp \
path_text_shape.cpp \
poi_symbol_shape.cpp \
@@ -171,6 +172,7 @@ HEADERS += \
my_position_controller.hpp \
navigator.hpp \
overlay_batcher.hpp \
+ overlays_tracker.hpp \
path_symbol_shape.hpp \
path_text_shape.hpp \
poi_symbol_shape.hpp \
diff --git a/drape_frontend/frontend_renderer.cpp b/drape_frontend/frontend_renderer.cpp
index 6f989572de..3cfb29892c 100755
--- a/drape_frontend/frontend_renderer.cpp
+++ b/drape_frontend/frontend_renderer.cpp
@@ -13,7 +13,6 @@
#include "drape_frontend/visual_params.hpp"
#include "drape_frontend/user_mark_shapes.hpp"
-
#include "drape/debug_rect_renderer.hpp"
#include "drape/shader_def.hpp"
#include "drape/support_manager.hpp"
@@ -114,7 +113,7 @@ struct RemoveTilePredicate
} // namespace
-FrontendRenderer::FrontendRenderer(Params const & params)
+FrontendRenderer::FrontendRenderer(Params && params)
: BaseRenderer(ThreadsCommutator::RenderThread, params)
, m_gpuProgramManager(new dp::GpuProgramManager())
, m_routeRenderer(new RouteRenderer())
@@ -138,6 +137,8 @@ FrontendRenderer::FrontendRenderer(Params const & params)
, m_needRestoreSize(false)
, m_needRegenerateTraffic(false)
, m_trafficEnabled(params.m_trafficEnabled)
+ , m_overlaysTracker(new OverlaysTracker())
+ , m_showEventCallback(move(params.m_showEventCallback))
#ifdef SCENARIO_ENABLE
, m_scenarioManager(new ScenarioManager(this))
#endif
@@ -776,8 +777,10 @@ void FrontendRenderer::AcceptMessage(ref_ptr<Message> message)
break;
}
- case Message::SetCustomSymbols:
+ case Message::UpdateCustomSymbols:
{
+ ref_ptr<UpdateCustomSymbolsMessage> msg = message;
+ m_overlaysTracker->Init(msg->AcceptSymbolsFeatures());
ScreenBase const & screen = m_userEventStream.GetCurrentScreen();
InvalidateRect(screen.ClipRect());
break;
@@ -1076,7 +1079,20 @@ void FrontendRenderer::UpdateOverlayTree(ScreenBase const & modelView, drape_ptr
void FrontendRenderer::EndUpdateOverlayTree()
{
if (m_overlayTree->IsNeedUpdate())
+ {
m_overlayTree->EndOverlayPlacing();
+
+ // Track overlays.
+ if (m_overlaysTracker->StartTracking(m_currentZoomLevel))
+ {
+ for (auto const & handle : m_overlayTree->GetHandlesCache())
+ {
+ if (handle->IsVisible())
+ m_overlaysTracker->Track(handle->GetOverlayID().m_featureId);
+ }
+ m_overlaysTracker->FinishTracking();
+ }
+ }
}
void FrontendRenderer::RenderScene(ScreenBase const & modelView)
@@ -1702,9 +1718,11 @@ void FrontendRenderer::Routine::Do()
m_renderer.OnContextCreate();
double const kMaxInactiveSeconds = 2.0;
+ double const kShowOverlaysEventsPeriod = 5.0;
my::Timer timer;
my::Timer activityTimer;
+ my::Timer showOverlaysEventsTimer;
double frameTime = 0.0;
bool modelViewChanged = true;
@@ -1772,15 +1790,25 @@ void FrontendRenderer::Routine::Do()
// Limit fps in following mode.
double constexpr kFrameTime = 1.0 / 30.0;
- if (isValidFrameTime && m_renderer.m_myPositionController->IsRouteFollowingActive() && frameTime < kFrameTime)
+ if (isValidFrameTime &&
+ m_renderer.m_myPositionController->IsRouteFollowingActive() && frameTime < kFrameTime)
{
uint32_t const ms = static_cast<uint32_t>((kFrameTime - frameTime) * 1000);
this_thread::sleep_for(milliseconds(ms));
}
+ if (m_renderer.m_overlaysTracker->IsValid() &&
+ showOverlaysEventsTimer.ElapsedSeconds() > kShowOverlaysEventsPeriod)
+ {
+ m_renderer.CollectShowOverlaysEvents();
+ showOverlaysEventsTimer.Reset();
+ }
+
m_renderer.CheckRenderingEnabled();
}
+ m_renderer.CollectShowOverlaysEvents();
+
#ifdef RENDER_DEBUG_RECTS
dp::DebugRectRenderer::Instance().Destroy();
#endif
@@ -1913,6 +1941,12 @@ drape_ptr<ScenarioManager> const & FrontendRenderer::GetScenarioManager() const
return m_scenarioManager;
}
+void FrontendRenderer::CollectShowOverlaysEvents()
+{
+ ASSERT(m_showEventCallback != nullptr, ());
+ m_showEventCallback(m_overlaysTracker->Collect());
+}
+
FrontendRenderer::RenderLayer::RenderLayerID FrontendRenderer::RenderLayer::GetLayerID(dp::GLState const & state)
{
if (state.GetDepthLayer() == dp::GLState::OverlayLayer)
diff --git a/drape_frontend/frontend_renderer.hpp b/drape_frontend/frontend_renderer.hpp
index e06ee32451..9a6512c084 100755
--- a/drape_frontend/frontend_renderer.hpp
+++ b/drape_frontend/frontend_renderer.hpp
@@ -10,6 +10,7 @@
#include "drape_frontend/gps_track_renderer.hpp"
#include "drape_frontend/my_position_controller.hpp"
#include "drape_frontend/navigator.hpp"
+#include "drape_frontend/overlays_tracker.hpp"
#include "drape_frontend/render_group.hpp"
#include "drape_frontend/requested_tiles.hpp"
#include "drape_frontend/route_renderer.hpp"
@@ -79,6 +80,7 @@ public:
location::TMyPositionModeChanged myPositionModeCallback,
location::EMyPositionMode initMode,
ref_ptr<RequestedTiles> requestedTiles,
+ OverlayShowEventCallback && showEventCallback,
double timeInBackground,
bool allow3dBuildings,
bool trafficEnabled,
@@ -94,6 +96,7 @@ public:
, m_myPositionModeCallback(myPositionModeCallback)
, m_initMyPositionMode(initMode)
, m_requestedTiles(requestedTiles)
+ , m_showEventCallback(move(showEventCallback))
, m_timeInBackground(timeInBackground)
, m_allow3dBuildings(allow3dBuildings)
, m_trafficEnabled(trafficEnabled)
@@ -110,6 +113,7 @@ public:
location::TMyPositionModeChanged m_myPositionModeCallback;
location::EMyPositionMode m_initMyPositionMode;
ref_ptr<RequestedTiles> m_requestedTiles;
+ OverlayShowEventCallback m_showEventCallback;
double m_timeInBackground;
bool m_allow3dBuildings;
bool m_trafficEnabled;
@@ -119,7 +123,7 @@ public:
bool m_isAutozoomEnabled;
};
- FrontendRenderer(Params const & params);
+ FrontendRenderer(Params && params);
~FrontendRenderer() override;
void Teardown();
@@ -241,6 +245,8 @@ private:
void OnCacheRouteArrows(int routeIndex, vector<ArrowBorders> const & borders);
+ void CollectShowOverlaysEvents();
+
drape_ptr<dp::GpuProgramManager> m_gpuProgramManager;
struct RenderLayer
@@ -330,6 +336,9 @@ private:
bool m_needRegenerateTraffic;
bool m_trafficEnabled;
+ drape_ptr<OverlaysTracker> m_overlaysTracker;
+ OverlayShowEventCallback m_showEventCallback;
+
drape_ptr<ScenarioManager> m_scenarioManager;
#ifdef DEBUG
diff --git a/drape_frontend/message.hpp b/drape_frontend/message.hpp
index ba6b5bde98..7c615e1674 100644
--- a/drape_frontend/message.hpp
+++ b/drape_frontend/message.hpp
@@ -76,6 +76,7 @@ public:
DrapeApiRemove,
DrapeApiFlush,
SetCustomSymbols,
+ UpdateCustomSymbols,
};
virtual ~Message() {}
diff --git a/drape_frontend/message_subclasses.hpp b/drape_frontend/message_subclasses.hpp
index a15245bd88..1052847afd 100644
--- a/drape_frontend/message_subclasses.hpp
+++ b/drape_frontend/message_subclasses.hpp
@@ -1155,7 +1155,7 @@ class SetCustomSymbolsMessage : public Message
{
public:
explicit SetCustomSymbolsMessage(CustomSymbols && symbols)
- : m_symbols(move(symbols))
+ : m_symbols(std::move(symbols))
{}
Type GetType() const override { return Message::SetCustomSymbols; }
@@ -1166,4 +1166,19 @@ private:
CustomSymbols m_symbols;
};
+class UpdateCustomSymbolsMessage : public Message
+{
+public:
+ explicit UpdateCustomSymbolsMessage(std::vector<FeatureID> && symbolsFeatures)
+ : m_symbolsFeatures(std::move(symbolsFeatures))
+ {}
+
+ Type GetType() const override { return Message::UpdateCustomSymbols; }
+
+ std::vector<FeatureID> && AcceptSymbolsFeatures() { return std::move(m_symbolsFeatures); }
+
+private:
+ std::vector<FeatureID> m_symbolsFeatures;
+};
+
} // namespace df
diff --git a/drape_frontend/overlays_tracker.cpp b/drape_frontend/overlays_tracker.cpp
new file mode 100644
index 0000000000..416fd5ec5e
--- /dev/null
+++ b/drape_frontend/overlays_tracker.cpp
@@ -0,0 +1,74 @@
+#include "drape_frontend/overlays_tracker.hpp"
+
+namespace df
+{
+void OverlaysTracker::Init(std::vector<FeatureID> && ids)
+{
+ m_data.clear();
+ for (auto const & fid : ids)
+ m_data.insert(std::make_pair(fid, OverlayInfo()));
+}
+
+bool OverlaysTracker::StartTracking(int zoomLevel)
+{
+ if (zoomLevel < kMinZoomLevel)
+ {
+ for (auto & p : m_data)
+ p.second.m_status = OverlayStatus::Invisible;
+ return false;
+ }
+
+ m_zoomLevel = zoomLevel;
+ for (auto & p : m_data)
+ p.second.m_tracked = false;
+
+ return true;
+}
+
+void OverlaysTracker::Track(FeatureID const & fid)
+{
+ ASSERT_GREATER_OR_EQUAL(m_zoomLevel, kMinZoomLevel, ());
+
+ auto it = m_data.find(fid);
+ if (it == m_data.end())
+ return;
+
+ it->second.m_tracked = true;
+ if (it->second.m_status == OverlayStatus::Invisible)
+ {
+ it->second.m_status = OverlayStatus::Visible;
+ m_events.emplace_back(it->first, m_zoomLevel, std::chrono::system_clock::now());
+ }
+}
+
+void OverlaysTracker::FinishTracking()
+{
+ ASSERT_GREATER_OR_EQUAL(m_zoomLevel, kMinZoomLevel, ());
+
+ for (auto & p : m_data)
+ {
+ if (p.second.m_status == OverlayStatus::Visible && !p.second.m_tracked)
+ {
+ p.second.m_status = OverlayStatus::InvisibleCandidate;
+ p.second.m_timestamp = std::chrono::steady_clock::now();
+ }
+ else if (p.second.m_status == OverlayStatus::InvisibleCandidate)
+ {
+ // Here we add some delay to avoid false events appearance.
+ static auto const kDelay = std::chrono::milliseconds(500);
+ if (p.second.m_tracked)
+ p.second.m_status = OverlayStatus::Visible;
+ else if (std::chrono::steady_clock::now() - p.second.m_timestamp > kDelay)
+ p.second.m_status = OverlayStatus::Invisible;
+ }
+ }
+ m_zoomLevel = -1;
+}
+
+std::list<OverlayShowEvent> OverlaysTracker::Collect()
+{
+ std::list<OverlayShowEvent> events;
+ std::swap(m_events, events);
+ return events;
+}
+} // namespace df
diff --git a/drape_frontend/overlays_tracker.hpp b/drape_frontend/overlays_tracker.hpp
new file mode 100644
index 0000000000..b09b1923e5
--- /dev/null
+++ b/drape_frontend/overlays_tracker.hpp
@@ -0,0 +1,65 @@
+#pragma once
+
+#include "indexer/feature_decl.hpp"
+
+#include <chrono>
+#include <functional>
+#include <list>
+#include <map>
+#include <vector>
+
+namespace df
+{
+struct OverlayShowEvent
+{
+ FeatureID m_feature;
+ int m_zoomLevel;
+ std::chrono::system_clock::time_point m_timestamp;
+ OverlayShowEvent(FeatureID const & feature, int zoomLevel,
+ std::chrono::system_clock::time_point const & timestamp)
+ : m_feature(feature)
+ , m_zoomLevel(zoomLevel)
+ , m_timestamp(timestamp)
+ {}
+};
+
+using OverlayShowEventCallback = std::function<void(std::list<OverlayShowEvent> &&)>;
+
+int constexpr kMinZoomLevel = 10;
+
+class OverlaysTracker
+{
+public:
+ OverlaysTracker() = default;
+
+ void Init(std::vector<FeatureID> && ids);
+
+ bool StartTracking(int zoomLevel);
+ void Track(FeatureID const & fid);
+ void FinishTracking();
+
+ std::list<OverlayShowEvent> Collect();
+
+ bool IsValid() const { return !m_data.empty(); }
+
+private:
+ enum class OverlayStatus
+ {
+ Invisible,
+ InvisibleCandidate,
+ Visible,
+ };
+
+ struct OverlayInfo
+ {
+ std::chrono::steady_clock::time_point m_timestamp;
+ OverlayStatus m_status = OverlayStatus::Invisible;
+ bool m_tracked = false;
+ };
+
+ std::map<FeatureID, OverlayInfo> m_data;
+ std::list<OverlayShowEvent> m_events;
+ int m_zoomLevel = -1;
+};
+
+} // namespace df
diff --git a/map/framework.cpp b/map/framework.cpp
index d95f1707b9..610c6350be 100644
--- a/map/framework.cpp
+++ b/map/framework.cpp
@@ -1673,6 +1673,13 @@ void Framework::CreateDrapeEngine(ref_ptr<dp::OGLContextFactory> contextFactory,
});
};
+ auto showOverlaysEventFn = [](std::list<df::OverlayShowEvent> && events)
+ {
+ // TODO: implement sending events. This callback is called on a render thread,
+ // so placing here not lightweight code is strictly prohibited! The best option is
+ // redirection events to another thread.
+ };
+
auto isCountryLoadedByNameFn = bind(&Framework::IsCountryLoadedByName, this, _1);
auto updateCurrentCountryFn = bind(&Framework::OnUpdateCurrentCountry, this, _1, _2);
@@ -1697,7 +1704,7 @@ void Framework::CreateDrapeEngine(ref_ptr<dp::OGLContextFactory> contextFactory,
move(myPositionModeChangedFn), allow3dBuildings, trafficEnabled, params.m_isChoosePositionMode,
params.m_isChoosePositionMode, GetSelectedFeatureTriangles(), params.m_isFirstLaunch,
m_routingSession.IsActive() && m_routingSession.IsFollowing(), isAutozoomEnabled,
- simplifiedTrafficColors);
+ simplifiedTrafficColors, showOverlaysEventFn);
m_drapeEngine = make_unique_dp<df::DrapeEngine>(move(p));
m_drapeEngine->SetModelViewListener([this](ScreenBase const & screen)
diff --git a/platform/platform_mac.mm b/platform/platform_mac.mm
index 02af7a74fc..278de9c535 100644
--- a/platform/platform_mac.mm
+++ b/platform/platform_mac.mm
@@ -54,6 +54,12 @@ Platform::Platform()
dataPath = "../../../../../../omim/data/";
if (IsFileExistsByFullPath(m_resourcesDir + dataPath))
m_writableDir = m_resourcesDir + dataPath;
+ if (m_writableDir.empty())
+ {
+ auto p = m_resourcesDir.find("/omim/");
+ if (p != std::string::npos)
+ m_writableDir = m_resourcesDir.substr(0, p) + "/omim/data/";
+ }
}
if (m_writableDir.empty())