From c4c866b1dea0e1205e9da45a0eb7c7d290dcaffb Mon Sep 17 00:00:00 2001 From: vng Date: Sun, 8 Jul 2012 14:49:35 -0700 Subject: [PATCH] Refactoring of maps update after downloading: - Delay maps deleting and updating if mwm is busy; - Fix bugs with different mwm maps status; - Add better function for country bounds; - Simplify Storage logic; --- defines.hpp | 1 + indexer/index.cpp | 83 +++++++++ indexer/index.hpp | 9 +- indexer/indexer_tests/mwm_set_test.cpp | 28 ++-- indexer/mwm_set.cpp | 143 +++++++++------- indexer/mwm_set.hpp | 79 +++++---- map/feature_vec_model.cpp | 20 ++- map/feature_vec_model.hpp | 10 +- map/framework.cpp | 84 ++++++++-- map/framework.hpp | 23 ++- qt/mainwindow.cpp | 2 +- qt/update_dialog.cpp | 125 +++++++------- qt/update_dialog.hpp | 14 +- search/search_engine.cpp | 5 + search/search_engine.hpp | 2 + storage/country.cpp | 22 ++- storage/country.hpp | 21 ++- storage/country_info.cpp | 102 +++++++++-- storage/country_info.hpp | 3 + storage/storage.cpp | 224 +++++-------------------- storage/storage.hpp | 36 +--- 21 files changed, 598 insertions(+), 438 deletions(-) diff --git a/defines.hpp b/defines.hpp index 0859f22dc9..6539d74931 100644 --- a/defines.hpp +++ b/defines.hpp @@ -10,6 +10,7 @@ #define HEADER_FILE_TAG "header" #define VERSION_FILE_TAG "version" +#define READY_FILE_EXTENSION ".ready" #define RESUME_FILE_EXTENSION ".resume2" #define DOWNLOADING_FILE_EXTENSION ".downloading2" diff --git a/indexer/index.cpp b/indexer/index.cpp index 42536b6d75..69de20d718 100644 --- a/indexer/index.cpp +++ b/indexer/index.cpp @@ -3,6 +3,8 @@ #include "../platform/platform.hpp" +#include "../coding/internal/file_data.hpp" + MwmValue::MwmValue(string const & name) : m_cont(GetPlatform().GetReader(name)), m_name(name) @@ -37,3 +39,84 @@ Index::~Index() { Cleanup(); } + +namespace +{ + void DeleteMapFiles(string const & path, bool deleteReady) + { + (void)my::DeleteFileX(path); + (void)my::DeleteFileX(path + RESUME_FILE_EXTENSION); + (void)my::DeleteFileX(path + DOWNLOADING_FILE_EXTENSION); + + if (deleteReady) + (void)my::DeleteFileX(path + READY_FILE_EXTENSION); + } + + string GetFullPath(string const & fileName) + { + return GetPlatform().WritablePathForFile(fileName); + } + + void ReplaceFileWithReady(string const & fileName) + { + string const path = GetFullPath(fileName); + DeleteMapFiles(path, false); + CHECK ( my::RenameFileX(path + READY_FILE_EXTENSION, path), (path) ); + } +} + +bool Index::DeleteMap(string const & fileName) +{ + threads::MutexGuard mutexGuard(m_lock); + UNUSED_VALUE(mutexGuard); + + if (!RemoveImpl(fileName)) + return false; + + DeleteMapFiles(GetFullPath(fileName), true); + return true; +} + +bool Index::UpdateMap(string const & fileName, m2::RectD & rect) +{ + threads::MutexGuard mutexGuard(m_lock); + UNUSED_VALUE(mutexGuard); + + MwmId const id = GetIdByName(fileName); + if (id != INVALID_MWM_ID) + { + m_info[id].m_status = MwmInfo::STATUS_UPDATE; + return false; + } + + ReplaceFileWithReady(fileName); + + (void)AddImpl(fileName, rect); + return true; +} + +void Index::UpdateMwmInfo(MwmId id) +{ + switch (m_info[id].m_status) + { + case MwmInfo::STATUS_TO_REMOVE: + if (m_info[id].m_lockCount == 0) + { + DeleteMapFiles(m_name[id], true); + + CHECK(RemoveImpl(id), ()); + } + break; + + case MwmInfo::STATUS_UPDATE: + if (m_info[id].m_lockCount == 0) + { + ClearCache(id); + + ReplaceFileWithReady(m_name[id]); + + m_info[id].m_status = MwmInfo::STATUS_ACTIVE; + } + break; + } +} diff --git a/indexer/index.hpp b/indexer/index.hpp index 4efb59116f..90cd3f0d1e 100644 --- a/indexer/index.hpp +++ b/indexer/index.hpp @@ -36,6 +36,7 @@ protected: /// @return mwm format version virtual int GetInfo(string const & name, MwmInfo & info) const; virtual MwmValue * CreateValue(string const & name) const; + virtual void UpdateMwmInfo(MwmId id); public: Index(); @@ -44,7 +45,8 @@ public: class MwmLock : public MwmSet::MwmLock { public: - MwmLock(Index const & index, MwmId mwmId) : MwmSet::MwmLock(index, mwmId) {} + MwmLock(Index const & index, MwmId mwmId) + : MwmSet::MwmLock(const_cast(index), mwmId) {} inline MwmValue * GetValue() const { @@ -55,6 +57,9 @@ public: inline feature::DataHeader const & GetHeader() const { return GetValue()->GetHeader(); } }; + bool DeleteMap(string const & fileName); + bool UpdateMap(string const & fileName, m2::RectD & rect); + template void ForEachInRect(F & f, m2::RectD const & rect, uint32_t scale) const { @@ -147,7 +152,7 @@ private: { /// @todo It's better to avoid hacks with scale comparison. - if (mwm[id].isCountry()) + if (mwm[id].IsCountry()) { // process countries first ProcessMwm(f, id, cov, scale); diff --git a/indexer/indexer_tests/mwm_set_test.cpp b/indexer/indexer_tests/mwm_set_test.cpp index 1e0df123e4..2f839ac3ab 100644 --- a/indexer/indexer_tests/mwm_set_test.cpp +++ b/indexer/indexer_tests/mwm_set_test.cpp @@ -42,10 +42,10 @@ UNIT_TEST(MwmSetSmokeTest) mwmSet.Remove("1"); mwmSet.GetMwmInfo(info); TEST_EQUAL(info.size(), 3, ()); - TEST(info[0].isValid(), ()); + TEST(info[0].IsActive(), ()); TEST_EQUAL(info[0].m_maxScale, 0, ()); - TEST(!info[1].isValid(), ()); - TEST(info[2].isValid(), ()); + TEST(!info[1].IsActive(), ()); + TEST(info[2].IsActive(), ()); TEST_EQUAL(info[2].m_maxScale, 2, ()); { MwmSet::MwmLock lock0(mwmSet, 0); @@ -57,11 +57,11 @@ UNIT_TEST(MwmSetSmokeTest) mwmSet.Add("3"); mwmSet.GetMwmInfo(info); TEST_EQUAL(info.size(), 3, ()); - TEST(info[0].isValid(), ()); + TEST(info[0].IsActive(), ()); TEST_EQUAL(info[0].m_maxScale, 0, ()); - TEST(info[1].isValid(), ()); + TEST(info[1].IsActive(), ()); TEST_EQUAL(info[1].m_maxScale, 3, ()); - TEST(info[2].isValid(), ()); + TEST(info[2].IsActive(), ()); TEST_EQUAL(info[2].m_maxScale, 2, ()); { @@ -72,23 +72,23 @@ UNIT_TEST(MwmSetSmokeTest) } mwmSet.GetMwmInfo(info); TEST_EQUAL(info.size(), 4, ()); - TEST(info[0].isValid(), ()); + TEST(info[0].IsActive(), ()); TEST_EQUAL(info[0].m_maxScale, 0, ()); - TEST(!info[1].isValid(), ()); - TEST(info[2].isValid(), ()); + TEST(!info[1].IsActive(), ()); + TEST(info[2].IsActive(), ()); TEST_EQUAL(info[2].m_maxScale, 2, ()); - TEST(info[3].isValid(), ()); + TEST(info[3].IsActive(), ()); TEST_EQUAL(info[3].m_maxScale, 4, ()); mwmSet.Add("5"); mwmSet.GetMwmInfo(info); TEST_EQUAL(info.size(), 4, ()); - TEST(info[0].isValid(), ()); + TEST(info[0].IsActive(), ()); TEST_EQUAL(info[0].m_maxScale, 0, ()); - TEST(info[1].isValid(), ()); + TEST(info[1].IsActive(), ()); TEST_EQUAL(info[1].m_maxScale, 5, ()); - TEST(info[2].isValid(), ()); + TEST(info[2].IsActive(), ()); TEST_EQUAL(info[2].m_maxScale, 2, ()); - TEST(info[3].isValid(), ()); + TEST(info[3].IsActive(), ()); TEST_EQUAL(info[3].m_maxScale, 4, ()); } diff --git a/indexer/mwm_set.cpp b/indexer/mwm_set.cpp index 8d763e08a8..dec88bf5df 100644 --- a/indexer/mwm_set.cpp +++ b/indexer/mwm_set.cpp @@ -10,34 +10,19 @@ #include "../std/memcpy.hpp" -namespace -{ - struct MwmIdIsEqualTo - { - MwmSet::MwmId m_id; - explicit MwmIdIsEqualTo(MwmSet::MwmId id) : m_id(id) {} - bool operator() (pair const & p) const - { - return p.first == m_id; - } - }; -} // unnamed namespace - MwmInfo::MwmInfo() : m_lockCount(0), m_status(STATUS_REMOVED) { // Important: STATUS_REMOVED - is the default value. // Apply STATUS_ACTIVE before adding to maps container. } -MwmSet::MwmLock::MwmLock(MwmSet const & mwmSet, MwmId mwmId) +MwmSet::MwmLock::MwmLock(MwmSet & mwmSet, MwmId mwmId) : m_mwmSet(mwmSet), m_id(mwmId), m_pValue(mwmSet.LockValue(mwmId)) { - //LOG(LINFO, ("MwmLock::MwmLock()", m_id)); } MwmSet::MwmLock::~MwmLock() { - //LOG(LINFO, ("MwmLock::~MwmLock()", m_id)); if (m_pValue) m_mwmSet.UnlockValue(m_id, m_pValue); } @@ -46,7 +31,6 @@ MwmSet::MwmLock::~MwmLock() MwmSet::MwmSet(size_t cacheSize) : m_cacheSize(cacheSize) { - //LOG(LINFO, ("MwmSet::MwmSet()")); } MwmSet::~MwmSet() @@ -60,14 +44,12 @@ void MwmSet::Cleanup() threads::MutexGuard mutexGuard(m_lock); UNUSED_VALUE(mutexGuard); - //LOG(LINFO, ("MwmSet::~MwmSet()")); - ClearCacheImpl(m_cache.begin(), m_cache.end()); #ifdef DEBUG - for (size_t i = 0; i < m_info.size(); ++i) + for (MwmId i = 0; i < m_info.size(); ++i) { - if (m_info[i].m_status == MwmInfo::STATUS_ACTIVE) + if (m_info[i].IsActive()) { ASSERT_EQUAL(m_info[i].m_lockCount, 0, (i, m_name[i])); ASSERT_NOT_EQUAL(m_name[i], string(), (i)); @@ -76,15 +58,15 @@ void MwmSet::Cleanup() #endif } -void MwmSet::UpdateMwmInfo(MwmInfo & info) +void MwmSet::UpdateMwmInfo(MwmId id) { - if (info.m_status == MwmInfo::STATUS_TO_REMOVE && info.m_lockCount == 0) - info.m_status = MwmInfo::STATUS_REMOVED; + if (m_info[id].m_status == MwmInfo::STATUS_TO_REMOVE) + (void)RemoveImpl(id); } MwmSet::MwmId MwmSet::GetFreeId() { - MwmId const size = static_cast(m_info.size()); + MwmId const size = m_info.size(); for (MwmId i = 0; i < size; ++i) { if (m_info[i].m_status == MwmInfo::STATUS_REMOVED) @@ -98,11 +80,10 @@ MwmSet::MwmId MwmSet::GetFreeId() MwmSet::MwmId MwmSet::GetIdByName(string const & name) { - MwmId const size = static_cast(m_info.size()); - for (MwmId i = 0; i < size; ++i) + for (MwmId i = 0; i < m_info.size(); ++i) { - UpdateMwmInfo(m_info[i]); - if (m_info[i].m_status == MwmInfo::STATUS_ACTIVE && m_name[i] == name) + UpdateMwmInfo(i); + if (m_name[i] == name) return i; } return INVALID_MWM_ID; @@ -115,19 +96,27 @@ string MwmSet::MwmLock::GetCountryName() const return src.substr(0, src.size() - strlen(DATA_FILE_EXTENSION)); } -int MwmSet::Add(string const & fileName, m2::RectD & r) +int MwmSet::Add(string const & fileName, m2::RectD & rect) { threads::MutexGuard mutexGuard(m_lock); UNUSED_VALUE(mutexGuard); - //LOG(LINFO, ("MwmSet::Add()", fileName)); - - if (GetIdByName(fileName) != INVALID_MWM_ID) + MwmId const id = GetIdByName(fileName); + if (id != INVALID_MWM_ID) { - LOG(LWARNING, ("Trying to add already added map", fileName)); + if (m_info[id].IsExist()) + LOG(LWARNING, ("Trying to add already added map", fileName)); + else + m_info[id].m_status = MwmInfo::STATUS_ACTIVE; + return -1; } + return AddImpl(fileName, rect); +} + +int MwmSet::AddImpl(string const & fileName, m2::RectD & rect) +{ // this function can throw an exception for bad mwm file MwmInfo info; int const version = GetInfo(fileName, info); @@ -138,18 +127,24 @@ int MwmSet::Add(string const & fileName, m2::RectD & r) m_name[id] = fileName; m_info[id] = info; - r = info.m_limitRect; - ASSERT ( r.IsValid(), () ); + rect = info.m_limitRect; + ASSERT ( rect.IsValid(), () ); return version; } -void MwmSet::RemoveImpl(MwmId id) +bool MwmSet::RemoveImpl(MwmId id) { if (m_info[id].m_lockCount == 0) + { + m_name[id].clear(); m_info[id].m_status = MwmInfo::STATUS_REMOVED; + return true; + } else + { m_info[id].m_status = MwmInfo::STATUS_TO_REMOVE; - m_name[id].clear(); + return false; + } } void MwmSet::Remove(string const & fileName) @@ -157,16 +152,22 @@ void MwmSet::Remove(string const & fileName) threads::MutexGuard mutexGuard(m_lock); UNUSED_VALUE(mutexGuard); - //LOG(LINFO, ("MwmSet::Remove()", fileName)); + (void)RemoveImpl(fileName); +} + +bool MwmSet::RemoveImpl(string const & fileName) +{ + bool ret = false; MwmId const id = GetIdByName(fileName); - if (id != INVALID_MWM_ID) + if (id != INVALID_MWM_ID && m_info[id].IsExist()) { - RemoveImpl(id); + ret = RemoveImpl(id); - // Update the cache. - ClearCacheImpl(RemoveIfKeepValid(m_cache.begin(), m_cache.end(), MwmIdIsEqualTo(id)), m_cache.end()); + ClearCache(id); } + + return ret; } void MwmSet::RemoveAllCountries() @@ -176,43 +177,49 @@ void MwmSet::RemoveAllCountries() for (MwmId i = 0; i < m_info.size(); ++i) { - if (m_info[i].isCountry()) - RemoveImpl(i); + if (m_info[i].IsCountry()) + (void)RemoveImpl(i); } // do not call ClearCache - it's under mutex lock ClearCacheImpl(m_cache.begin(), m_cache.end()); } -bool MwmSet::IsLoaded(string const & fName) const +bool MwmSet::IsLoaded(string const & file) const { - return (const_cast(this)->GetIdByName(fName + DATA_FILE_EXTENSION) != INVALID_MWM_ID); + MwmSet * p = const_cast(this); + + threads::MutexGuard mutexGuard(p->m_lock); + UNUSED_VALUE(mutexGuard); + + MwmId const id = p->GetIdByName(file + DATA_FILE_EXTENSION); + return (id != INVALID_MWM_ID && m_info[id].IsExist()); } void MwmSet::GetMwmInfo(vector & info) const { - threads::MutexGuard mutexGuard(m_lock); + MwmSet * p = const_cast(this); + + threads::MutexGuard mutexGuard(p->m_lock); UNUSED_VALUE(mutexGuard); - for (vector::iterator it = m_info.begin(); it != m_info.end(); ++it) - UpdateMwmInfo(*it); + for (MwmId i = 0; i < m_info.size(); ++i) + p->UpdateMwmInfo(i); info = m_info; } -MwmSet::MwmValueBase * MwmSet::LockValue(MwmId id) const +MwmSet::MwmValueBase * MwmSet::LockValue(MwmId id) { threads::MutexGuard mutexGuard(m_lock); UNUSED_VALUE(mutexGuard); - //LOG(LINFO, ("MwmSet::LockContainer()", id)); - ASSERT_LESS(id, m_info.size(), ()); if (id >= m_info.size()) return NULL; - UpdateMwmInfo(m_info[id]); - if (m_info[id].m_status != MwmInfo::STATUS_ACTIVE) + UpdateMwmInfo(id); + if (!m_info[id].IsActive()) return NULL; ++m_info[id].m_lockCount; @@ -230,13 +237,11 @@ MwmSet::MwmValueBase * MwmSet::LockValue(MwmId id) const return CreateValue(m_name[id]); } -void MwmSet::UnlockValue(MwmId id, MwmValueBase * p) const +void MwmSet::UnlockValue(MwmId id, MwmValueBase * p) { threads::MutexGuard mutexGuard(m_lock); UNUSED_VALUE(mutexGuard); - //LOG(LINFO, ("MwmSet::UnlockContainer()", id)); - ASSERT(p, (id)); ASSERT_LESS(id, m_info.size(), ()); if (id >= m_info.size() || p == 0) @@ -245,9 +250,9 @@ void MwmSet::UnlockValue(MwmId id, MwmValueBase * p) const ASSERT_GREATER(m_info[id].m_lockCount, 0, ()); if (m_info[id].m_lockCount > 0) --m_info[id].m_lockCount; - UpdateMwmInfo(m_info[id]); + UpdateMwmInfo(id); - if (m_info[id].m_status == MwmInfo::STATUS_ACTIVE) + if (m_info[id].IsActive()) { m_cache.push_back(make_pair(id, p)); if (m_cache.size() > m_cacheSize) @@ -275,3 +280,21 @@ void MwmSet::ClearCacheImpl(CacheType::iterator beg, CacheType::iterator end) delete it->second; m_cache.erase(beg, end); } + +namespace +{ + struct MwmIdIsEqualTo + { + MwmSet::MwmId m_id; + explicit MwmIdIsEqualTo(MwmSet::MwmId id) : m_id(id) {} + bool operator() (pair const & p) const + { + return (p.first == m_id); + } + }; +} + +void MwmSet::ClearCache(MwmId id) +{ + ClearCacheImpl(RemoveIfKeepValid(m_cache.begin(), m_cache.end(), MwmIdIsEqualTo(id)), m_cache.end()); +} diff --git a/indexer/mwm_set.hpp b/indexer/mwm_set.hpp index 00235b7604..5bde1d3ae3 100644 --- a/indexer/mwm_set.hpp +++ b/indexer/mwm_set.hpp @@ -19,14 +19,21 @@ public: uint8_t m_minScale; ///< Min zoom level of mwm. uint8_t m_maxScale; ///< Max zoom level of mwm. - // Does this MwmInfo represent a valid Mwm? - inline bool isValid() const { return (m_status == STATUS_ACTIVE); } - inline bool isCountry() const { return (m_minScale > 0); } + inline bool IsExist() const + { + return (m_status == STATUS_ACTIVE || m_status == STATUS_UPDATE); + } + inline bool IsCountry() const { return (m_minScale > 0); } + inline bool IsActive() const { return (m_status == STATUS_ACTIVE); } -private: - friend class MwmSet; + enum Status + { + STATUS_ACTIVE = 0, + STATUS_TO_REMOVE, + STATUS_REMOVED, + STATUS_UPDATE + }; - enum Status { STATUS_ACTIVE = 0, STATUS_TO_REMOVE = 1, STATUS_REMOVED = 2 }; uint8_t m_lockCount; ///< Number of locks. uint8_t m_status; ///< Current country status. }; @@ -49,7 +56,7 @@ public: class MwmLock { public: - MwmLock(MwmSet const & mwmSet, MwmId mwmId); + MwmLock(MwmSet & mwmSet, MwmId mwmId); ~MwmLock(); inline MwmValueBase * GetValue() const { return m_pValue; } @@ -57,26 +64,33 @@ public: inline MwmId GetID() const { return m_id; } private: - MwmSet const & m_mwmSet; + MwmSet & m_mwmSet; MwmId m_id; MwmValueBase * m_pValue; }; /// Add new mwm. Returns false, if mwm with given fileName already exists. /// @param[in] fileName File name (without full path) of country. - /// @param[out] r Limit rect of country. + /// @param[out] rect Limit rect of country. /// @return Map format version or -1 if not added - int Add(string const & fileName, m2::RectD & r); + //@{ +protected: + int AddImpl(string const & fileName, m2::RectD & rect); + +public: + int Add(string const & fileName, m2::RectD & rect); inline bool Add(string const & fileName) { m2::RectD dummy; return (-1 != Add(fileName, dummy)); } + //@} /// @name Remove mwm. //@{ -private: - void RemoveImpl(MwmId id); +protected: + bool RemoveImpl(MwmId id); + bool RemoveImpl(string const & fileName); public: void Remove(string const & fileName); @@ -84,9 +98,11 @@ public: void RemoveAllCountries(); //@} - bool IsLoaded(string const & fName) const; + /// @param[in] file File name without extension. + bool IsLoaded(string const & file) const; - // Get ids of all mwms. Some of them may be marked to remove. + /// Get ids of all mwms. Some of them may be with not active status. + /// In that case, LockValue returns NULL. void GetMwmInfo(vector & info) const; // Clear caches. @@ -100,28 +116,35 @@ protected: void Cleanup(); private: - static const MwmId INVALID_MWM_ID = static_cast(-1); - typedef deque > CacheType; - // Update given MwmInfo. - inline static void UpdateMwmInfo(MwmInfo & info); - - MwmValueBase * LockValue(MwmId id) const; - void UnlockValue(MwmId id, MwmValueBase * p) const; + MwmValueBase * LockValue(MwmId id); + void UnlockValue(MwmId id, MwmValueBase * p); // Find first removed mwm or add a new one. MwmId GetFreeId(); - // Find mwm with a given name. - MwmId GetIdByName(string const & name); - // Do the cleaning for [beg, end) without acquiring the mutex. void ClearCacheImpl(CacheType::iterator beg, CacheType::iterator end); - mutable vector m_info; // mutable needed for GetMwmInfo - /*mutable*/ vector m_name; - mutable CacheType m_cache; + CacheType m_cache; size_t m_cacheSize; - mutable threads::Mutex m_lock; + +protected: + static const MwmId INVALID_MWM_ID = static_cast(-1); + + /// Find mwm with a given name. + /// @note This function is always called under mutex m_lock. + MwmId GetIdByName(string const & name); + + /// @note This function is always called under mutex m_lock. + void ClearCache(MwmId id); + + /// Update given MwmInfo. + /// @note This function is always called under mutex m_lock. + virtual void UpdateMwmInfo(MwmId id); + + vector m_info; + vector m_name; + threads::Mutex m_lock; }; diff --git a/map/feature_vec_model.cpp b/map/feature_vec_model.cpp index 875ae178d0..9dd8e52455 100644 --- a/map/feature_vec_model.cpp +++ b/map/feature_vec_model.cpp @@ -53,9 +53,19 @@ int FeaturesFetcher::AddMap(string const & file) return version; } -void FeaturesFetcher::RemoveMap(string const & fName) +void FeaturesFetcher::RemoveMap(string const & file) { - m_multiIndex.Remove(fName); + m_multiIndex.Remove(file); +} + +bool FeaturesFetcher::DeleteMap(string const & file) +{ + return m_multiIndex.DeleteMap(file); +} + +bool FeaturesFetcher::UpdateMap(string const & file, m2::RectD & rect) +{ + return m_multiIndex.UpdateMap(file, rect); } void FeaturesFetcher::RemoveAllCountries() @@ -74,13 +84,14 @@ void FeaturesFetcher::ClearCaches() m_multiIndex.ClearCache(); } -bool FeaturesFetcher::IsCountryLoaded(m2::PointD const & pt) const +/* +bool FeaturesFetcher::IsLoaded(m2::PointD const & pt) const { vector info; m_multiIndex.GetMwmInfo(info); for (size_t i = 0; i < info.size(); ++i) - if (info[i].isValid() && info[i].isCountry() && + if (info[i].IsExist() && info[i].IsCountry() && info[i].m_limitRect.IsPointInside(pt)) { return true; @@ -88,6 +99,7 @@ bool FeaturesFetcher::IsCountryLoaded(m2::PointD const & pt) const return false; } +*/ m2::RectD FeaturesFetcher::GetWorldRect() const { diff --git a/map/feature_vec_model.hpp b/map/feature_vec_model.hpp index 6edcad28f9..7232cf83de 100644 --- a/map/feature_vec_model.hpp +++ b/map/feature_vec_model.hpp @@ -33,11 +33,17 @@ namespace model public: void InitClassificator(); + /// @param[in] file Name of mwm file with extension. + //@{ /// @return MWM format version for file or -1 if error and map was not added int AddMap(string const & file); - void RemoveMap(string const & fName); + void RemoveMap(string const & file); void RemoveAllCountries(); + bool DeleteMap(string const & file); + bool UpdateMap(string const & file, m2::RectD & rect); + //@} + //void Clean(); void ClearCaches(); @@ -46,7 +52,7 @@ namespace model return m_multiIndex.IsLoaded(fName); } - bool IsCountryLoaded(m2::PointD const & pt) const; + //bool IsLoaded(m2::PointD const & pt) const; /// @name Features enumeration. //@{ diff --git a/map/framework.cpp b/map/framework.cpp index 53f6aa2122..447c91fdad 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -87,7 +87,7 @@ void Framework::OnGpsUpdate(location::GpsInfo const & info) // correct rect scale if country isn't downloaded int const upperScale = scales::GetUpperWorldScale(); - if (rectScale > upperScale && IsEmptyModel(center)) + if (rectScale > upperScale && IsCountryLoaded(center)) { setScale = upperScale; } @@ -202,9 +202,7 @@ Framework::Framework() for_each(maps.begin(), maps.end(), bind(&Framework::AddMap, this, _1)); - m_storage.Init(bind(&Framework::AddMap, this, _1), - bind(&Framework::RemoveMap, this, _1), - bind(&Framework::InvalidateRect, this, _1, true)); + m_storage.Init(bind(&Framework::UpdateAfterDownload, this, _1)); LOG(LDEBUG, ("Storage initialized")); } @@ -213,6 +211,68 @@ Framework::~Framework() ClearBookmarks(); } +void Framework::DeleteMap(storage::TIndex const & index) +{ + if (!m_storage.DeleteFromDownloader(index)) + { + string const & file = m_storage.CountryByIndex(index).GetFile().m_fileName; + if (m_model.DeleteMap(file + DATA_FILE_EXTENSION)) + InvalidateRect(GetCountryBounds(file), true); + } + + m_storage.NotifyStatusChanged(index); +} + +storage::TStatus Framework::GetCountryStatus(storage::TIndex const & index) const +{ + using namespace storage; + + storage::TStatus res = m_storage.CountryStatus(index); + + if (res == EUnknown) + { + Country const & c = m_storage.CountryByIndex(index); + LocalAndRemoteSizeT const size = c.Size(); + + if (size.first == 0) + return ENotDownloaded; + + if (size.second == 0) + return EUnknown; + + res = EOnDisk; + if (size.first != size.second) + { + /// @todo Do better version check, not just size comparison. + + // Additional check for .ready file. + // Use EOnDisk status if it's good, or EOnDiskOutOfDate otherwise. + Platform const & pl = GetPlatform(); + string const fName = pl.WritablePathForFile(c.GetFile().GetFileWithExt() + READY_FILE_EXTENSION); + + uint64_t sz = 0; + if (!pl.GetFileSizeByFullPath(fName, sz) || sz != size.second) + res = EOnDiskOutOfDate; + } + } + + return res; +} + +m2::RectD Framework::GetCountryBounds(string const & file) +{ + m2::RectD const r = GetSearchEngine()->GetCountryBounds(file); + ASSERT ( r.IsValid(), () ); + return r; +} + +void Framework::UpdateAfterDownload(string const & file) +{ + m2::RectD rect; + if (m_model.UpdateMap(file, rect)) + InvalidateRect(rect, true); +} + void Framework::AddLocalMaps() { // initializes model with locally downloaded maps @@ -364,6 +424,7 @@ void Framework::InvalidateRect(m2::RectD const & rect, bool doForceUpdate) { if (m_renderPolicy) { + ASSERT ( rect.IsValid(), () ); m_renderPolicy->SetForceUpdate(doForceUpdate); m_renderPolicy->SetInvalidRect(m2::AnyRectD(rect)); m_renderPolicy->GetWindowHandle()->invalidate(); @@ -483,18 +544,14 @@ void Framework::DrawModel(shared_ptr const & e, Invalidate(); } -bool Framework::IsEmptyModel(m2::PointD const & pt) +bool Framework::IsCountryLoaded(m2::PointD const & pt) { // Correct, but slow version (check country polygon). string const fName = GetSearchEngine()->GetCountryFile(pt); if (fName.empty()) - return false; + return true; - return !m_model.IsLoaded(fName); - // Fast, but not strict-correct version (just check limit rect). - // *Upd* Doesn't work in many cases, as there are a lot of countries which has limit rect - // that even roughly doesn't correspond to the shape of the country. - // return !m_model.IsCountryLoaded(pt); + return m_model.IsLoaded(fName); } void Framework::BeginPaint(shared_ptr const & e) @@ -920,11 +977,10 @@ void Framework::ShowSearchResult(search::Result const & res) if (scales::GetScaleLevel(r) > scales::GetUpperWorldScale()) { m2::PointD const c = r.Center(); - if (!m_model.IsCountryLoaded(c)) + if (!IsCountryLoaded(c)) r = scales::GetRectForLevel(scales::GetUpperWorldScale(), c, 1.0); } - /// @todo We can't call this fucntion in android because of invalid m_renderPolicy. ShowRectFixed(r); DrawPlacemark(res.GetFeatureCenter()); @@ -967,7 +1023,7 @@ void Framework::SetRenderPolicy(RenderPolicy * renderPolicy) if (m_width != 0 && m_height != 0) OnSize(m_width, m_height); - // Do full invalidate except of any "pending" stuff. + // Do full invalidate instead of any "pending" stuff. Invalidate(); /* diff --git a/map/framework.hpp b/map/framework.hpp index 29381eb833..e857b61d89 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -105,6 +105,10 @@ protected: scoped_ptr m_guiController; InformationDisplay m_informationDisplay; + /// This function is called by m_storage to notify that country downloading is finished. + /// @param[in] file Country file name (without extensions). + void UpdateAfterDownload(string const & file); + //my::Timer m_timer; inline double ElapsedSeconds() const { @@ -132,11 +136,21 @@ public: void AddMap(string const & file); void RemoveMap(string const & datFile); - /// Only file names - void GetLocalMaps(vector & outMaps) const; - void AddLocalMaps(); void RemoveLocalMaps(); + /// @return File names without path. + void GetLocalMaps(vector & outMaps) const; + + /// @name This functions is used by Downloader UI. + //@{ + void DeleteMap(storage::TIndex const & index); + + storage::TStatus GetCountryStatus(storage::TIndex const & index) const; + + /// Get country rect from borders (not from mwm file). + /// @param[in] file Pass country file name without extension as an id. + m2::RectD GetCountryBounds(string const & file); + //@} void AddBookmark(string const & category, Bookmark const & bm); inline size_t GetBmCategoriesCount() const { return m_bookmarks.size(); } @@ -323,6 +337,5 @@ public: } private: - //bool IsEmptyModel() const; - bool IsEmptyModel(m2::PointD const & pt); + bool IsCountryLoaded(m2::PointD const & pt); }; diff --git a/qt/mainwindow.cpp b/qt/mainwindow.cpp index a28f7e6c0f..9f888a0f85 100644 --- a/qt/mainwindow.cpp +++ b/qt/mainwindow.cpp @@ -366,7 +366,7 @@ void MainWindow::OnPreferences() #ifndef NO_DOWNLOADER void MainWindow::ShowUpdateDialog() { - UpdateDialog dlg(this, m_pDrawWidget->GetFramework().Storage()); + UpdateDialog dlg(this, m_pDrawWidget->GetFramework()); dlg.ShowModal(); } diff --git a/qt/update_dialog.cpp b/qt/update_dialog.cpp index a05e34c92b..eab6537409 100644 --- a/qt/update_dialog.cpp +++ b/qt/update_dialog.cpp @@ -54,9 +54,9 @@ namespace qt }; - UpdateDialog::UpdateDialog(QWidget * parent, Storage & storage) + UpdateDialog::UpdateDialog(QWidget * parent, Framework & framework) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), - m_storage(storage), + m_framework(framework), m_observerSlotId(0) { setWindowModality(Qt::WindowModal); @@ -85,14 +85,14 @@ namespace qt resize(600, 500); // we want to receive all download progress and result events - m_observerSlotId = m_storage.Subscribe(bind(&UpdateDialog::OnCountryChanged, this, _1), - bind(&UpdateDialog::OnCountryDownloadProgress, this, _1, _2)); + m_observerSlotId = GetStorage().Subscribe(bind(&UpdateDialog::OnCountryChanged, this, _1), + bind(&UpdateDialog::OnCountryDownloadProgress, this, _1, _2)); } UpdateDialog::~UpdateDialog() { // tell download manager that we're gone... - m_storage.Unsubscribe(m_observerSlotId); + GetStorage().Unsubscribe(m_observerSlotId); } /// when user clicks on any map row in the table @@ -112,7 +112,8 @@ namespace qt } TIndex const countryIndex(treeIndex[0], treeIndex[1], treeIndex[2]); - switch (m_storage.CountryStatus(countryIndex)) + Storage & st = GetStorage(); + switch (m_framework.GetCountryStatus(countryIndex)) { case EOnDiskOutOfDate: { @@ -129,11 +130,12 @@ namespace qt (void)ask.exec(); QAbstractButton * res = ask.clickedButton(); - if (res == btns[0] || res == btns[1]) - m_storage.DeleteCountry(countryIndex); if (res == btns[0]) - m_storage.DownloadCountry(countryIndex); + st.DownloadCountry(countryIndex); + + if (res == btns[1]) + m_framework.DeleteMap(countryIndex); } break; @@ -147,22 +149,18 @@ namespace qt ask.setDefaultButton(QMessageBox::No); if (ask.exec() == QMessageBox::Yes) - m_storage.DeleteCountry(countryIndex); + m_framework.DeleteMap(countryIndex); } break; case ENotDownloaded: case EDownloadFailed: - m_storage.DownloadCountry(countryIndex); + st.DownloadCountry(countryIndex); break; case EInQueue: case EDownloading: - m_storage.DeleteCountry(countryIndex); - break; - - case EGeneratingIndex: - // we can't stop index genertion at this moment + m_framework.DeleteMap(countryIndex); break; } } @@ -227,30 +225,31 @@ namespace qt QString statusString; LocalAndRemoteSizeT size(0, 0); - switch (m_storage.CountryStatus(index)) + Storage const & st = GetStorage(); + switch (m_framework.GetCountryStatus(index)) { case ENotDownloaded: statusString = tr("Click to download"); rowColor = COLOR_NOTDOWNLOADED; - size = m_storage.CountrySizeInBytes(index); + size = st.CountrySizeInBytes(index); break; case EOnDisk: statusString = tr("Installed (click to delete)"); rowColor = COLOR_ONDISK; - size = m_storage.CountrySizeInBytes(index); + size = st.CountrySizeInBytes(index); break; case EOnDiskOutOfDate: statusString = tr("Out of date (click to update or delete)"); rowColor = COLOR_OUTOFDATE; - size = m_storage.CountrySizeInBytes(index); + size = st.CountrySizeInBytes(index); break; case EDownloadFailed: statusString = tr("Download has failed"); rowColor = COLOR_DOWNLOADFAILED; - size = m_storage.CountrySizeInBytes(index); + size = st.CountrySizeInBytes(index); break; case EDownloading: @@ -261,12 +260,7 @@ namespace qt case EInQueue: statusString = tr("Marked for download"); rowColor = COLOR_INQUEUE; - size = m_storage.CountrySizeInBytes(index); - break; - - case EGeneratingIndex: - statusString = tr("Generating search index ..."); - rowColor = COLOR_INPROGRESS; + size = st.CountrySizeInBytes(index); break; } @@ -275,15 +269,20 @@ namespace qt if (size.second > 0) { - if (size.second > 1000 * 1000 * 1000) - item->setText(KColumnIndexSize, QString("%1/%2 GB").arg( - uint(size.first / (1000 * 1000 * 1000))).arg(uint(size.second / (1000 * 1000 * 1000)))); - else if (size.second > 1000 * 1000) + int const halfMb = 512 * 1024; + int const Mb = 1024 * 1024; + + if (size.second > Mb) + { item->setText(KColumnIndexSize, QString("%1/%2 MB").arg( - uint(size.first / (1000 * 1000))).arg(uint(size.second / (1000 * 1000)))); + uint((size.first + halfMb) / Mb)).arg(uint((size.second + halfMb) / Mb))); + } else + { item->setText(KColumnIndexSize, QString("%1/%2 kB").arg( - uint((size.first + 999) / 1000)).arg(uint((size.second + 999) / 1000))); + uint((size.first + 1023) / 1024)).arg(uint((size.second + 1023) / 1024))); + } + // needed for column sorting item->setData(KColumnIndexSize, Qt::UserRole, QVariant(qint64(size.second))); } @@ -293,45 +292,50 @@ namespace qt } } + QTreeWidgetItem * UpdateDialog::CreateTreeItem(TIndex const & index, int value, QTreeWidgetItem * parent) + { + QString const text = QString::fromUtf8(GetStorage().CountryName(index).c_str()); + QTreeWidgetItem * item = new QTreeWidgetItem(parent, QStringList(text)); + item->setData(KColumnIndexCountry, Qt::UserRole, QVariant(value)); + + if (parent == 0) + m_tree->addTopLevelItem(item); + return item; + } + + int UpdateDialog::GetChildsCount(TIndex const & index) const + { + return static_cast(GetStorage().CountriesCount(index)); + } + void UpdateDialog::FillTree() { m_tree->setSortingEnabled(false); m_tree->clear(); - for (int group = 0; group < static_cast(m_storage.CountriesCount(TIndex())); ++group) + int const gCount = GetChildsCount(TIndex()); + for (int group = 0; group < gCount; ++group) { TIndex const grIndex(group); - QStringList groupText(QString::fromUtf8(m_storage.CountryName(grIndex).c_str())); - QTreeWidgetItem * groupItem = new QTreeWidgetItem(groupText); - groupItem->setData(KColumnIndexCountry, Qt::UserRole, QVariant(group)); - m_tree->addTopLevelItem(groupItem); - // set color by status and update country size + QTreeWidgetItem * groupItem = CreateTreeItem(grIndex, group, 0); UpdateRowWithCountryInfo(grIndex); - for (int country = 0; country < static_cast(m_storage.CountriesCount(grIndex)); ++country) + int const cCount = GetChildsCount(grIndex); + for (int country = 0; country < cCount; ++country) { - TIndex cIndex(group, country); - QStringList countryText(QString::fromUtf8(m_storage.CountryName(cIndex).c_str())); - QTreeWidgetItem * countryItem = new QTreeWidgetItem(groupItem, countryText); - countryItem->setData(KColumnIndexCountry, Qt::UserRole, QVariant(country)); - // set color by status and update country size + TIndex const cIndex(group, country); + QTreeWidgetItem * countryItem = CreateTreeItem(cIndex, country, groupItem); UpdateRowWithCountryInfo(cIndex); - for (int region = 0; region < static_cast(m_storage.CountriesCount(cIndex)); ++region) + int const rCount = GetChildsCount(cIndex); + for (int region = 0; region < rCount; ++region) { TIndex const rIndex(group, country, region); - QStringList regionText(QString::fromUtf8(m_storage.CountryName(rIndex).c_str())); - QTreeWidgetItem * regionItem = new QTreeWidgetItem(countryItem, regionText); - regionItem->setData(KColumnIndexCountry, Qt::UserRole, QVariant(region)); - // set color by status and update country size + (void)CreateTreeItem(rIndex, region, countryItem); UpdateRowWithCountryInfo(rIndex); } } } -// // Size column, actual size will be set later -// QTableWidgetItemWithCustomSorting * sizeItem = new QTableWidgetItemWithCustomSorting; -// sizeItem->setFlags(sizeItem->flags() ^ Qt::ItemIsEditable); -// m_table->setItem(row, KItemIndexSize, sizeItem); m_tree->sortByColumn(KColumnIndexCountry, Qt::AscendingOrder); m_tree->setSortingEnabled(true); @@ -349,20 +353,7 @@ namespace qt { QTreeWidgetItem * item = GetTreeItemByIndex(*m_tree, index); if (item) - { -// QString speed; -// if (progress.m_bytesPerSec > 1000 * 1000) -// speed = QString(" %1 MB/s").arg(QString::number(static_cast(progress.m_bytesPerSec) / (1000.0 * 1000.0), -// 'f', 1)); -// else if (progress.m_bytesPerSec > 1000) -// speed = QString(" %1 kB/s").arg(progress.m_bytesPerSec / 1000); -// else if (progress.m_bytesPerSec >= 0) -// speed = QString(" %1 B/sec").arg(progress.m_bytesPerSec); - item->setText(KColumnIndexSize, QString("%1%").arg(progress.first * 100 / progress.second)); -// item->setText(KColumnIndexSize, QString("%1%%2").arg(progress.m_current * 100 / progress.m_total) -// .arg(speed)); - } } void UpdateDialog::ShowModal() diff --git a/qt/update_dialog.hpp b/qt/update_dialog.hpp index e02c7729e1..829e9eed49 100644 --- a/qt/update_dialog.hpp +++ b/qt/update_dialog.hpp @@ -1,6 +1,5 @@ #pragma once - -#include "../storage/storage.hpp" +#include "../map/framework.hpp" #include @@ -9,6 +8,8 @@ class QTreeWidgetItem; class QLabel; class QPushButton; +class Framework; + namespace qt { class UpdateDialog : public QDialog @@ -16,7 +17,7 @@ namespace qt Q_OBJECT public: - explicit UpdateDialog(QWidget * parent, storage::Storage & storage); + explicit UpdateDialog(QWidget * parent, Framework & framework); virtual ~UpdateDialog(); /// @name Called from downloader to notify GUI @@ -36,9 +37,14 @@ namespace qt void FillTree(); void UpdateRowWithCountryInfo(storage::TIndex const & index); + QTreeWidgetItem * CreateTreeItem(storage::TIndex const & index, int value, QTreeWidgetItem * parent); + int GetChildsCount(storage::TIndex const & index) const; + private: + inline storage::Storage & GetStorage() const { return m_framework.Storage(); } + QTreeWidget * m_tree; - storage::Storage & m_storage; + Framework & m_framework; int m_observerSlotId; }; } // namespace qt diff --git a/search/search_engine.cpp b/search/search_engine.cpp index 5a57abcc76..1a96aceb0c 100644 --- a/search/search_engine.cpp +++ b/search/search_engine.cpp @@ -329,6 +329,11 @@ bool Engine::GetNameByType(uint32_t type, int8_t lang, string & name) const return m_pData->m_categories.GetNameByType(type, lang, name); } +m2::RectD Engine::GetCountryBounds(string const & file) const +{ + return m_pData->m_infoGetter.CalcLimitRect(file); +} + void Engine::ClearCaches() { /// @todo Add m_pData->m_infoGetter clearing routine. diff --git a/search/search_engine.hpp b/search/search_engine.hpp index e38a3a4290..7a20b703af 100644 --- a/search/search_engine.hpp +++ b/search/search_engine.hpp @@ -51,6 +51,8 @@ public: bool GetNameByType(uint32_t type, int8_t lang, string & name) const; + m2::RectD GetCountryBounds(string const & file) const; + void ClearCaches(); private: diff --git a/storage/country.cpp b/storage/country.cpp index 054b66bcef..af21ffc747 100644 --- a/storage/country.cpp +++ b/storage/country.cpp @@ -1,8 +1,8 @@ #include "country.hpp" -#include "../indexer/data_factory.hpp" +//#include "../indexer/data_factory.hpp" -#include "../coding/file_container.hpp" +//#include "../coding/file_container.hpp" #include "../platform/platform.hpp" @@ -29,6 +29,7 @@ uint32_t CountryFile::GetFileSize() const return 0; } +/* class CountryBoundsCalculator { m2::RectD & m_bounds; @@ -54,6 +55,7 @@ m2::RectD Country::Bounds() const std::for_each(m_files.begin(), m_files.end(), CountryBoundsCalculator(bounds)); return bounds; } +*/ LocalAndRemoteSizeT Country::Size() const { @@ -234,6 +236,7 @@ void SaveImpl(T const & v, json_t * jParent) { size_t const siblingsCount = v.SiblingsCount(); CHECK_GREATER(siblingsCount, 0, ()); + my::Json jArray(json_array()); for (size_t i = 0; i < siblingsCount; ++i) { @@ -244,22 +247,27 @@ void SaveImpl(T const & v, json_t * jParent) string const strFlag = v[i].Value().Flag(); if (!strFlag.empty()) json_object_set_new(jCountry, "c", json_string(strFlag.c_str())); - CHECK_LESS_OR_EQUAL(v[i].Value().Files().size(), 1, ("Not supporting more than 1 file for the country at the moment")); - if (v[i].Value().Files().size()) + + size_t countriesCount = v[i].Value().GetFilesCount(); + ASSERT_LESS_OR_EQUAL(countriesCount, 1, ()); + if (countriesCount > 0) { - int64_t const price = v[i].Value().Files()[0].m_price; + CountryFile const & file = v[i].Value().GetFile(); + int64_t const price = file.m_price; CHECK_GREATER_OR_EQUAL(price, 0, ("Invalid price")); json_object_set_new(jCountry, "p", json_integer(price)); - string const strFile = v[i].Value().Files()[0].m_fileName; + string const strFile = file.m_fileName; if (strFile != strName) json_object_set_new(jCountry, "f", json_string(strFile.c_str())); - json_object_set_new(jCountry, "s", json_integer(v[i].Value().Files()[0].m_remoteSize)); + json_object_set_new(jCountry, "s", json_integer(file.m_remoteSize)); } + if (v[i].SiblingsCount()) SaveImpl(v[i], jCountry); json_array_append(jArray, jCountry); } + json_object_set(jParent, "g", jArray); } diff --git a/storage/country.hpp b/storage/country.hpp index acf9e8ddfa..d927d72fdc 100644 --- a/storage/country.hpp +++ b/storage/country.hpp @@ -57,14 +57,31 @@ namespace storage bool operator<(Country const & other) const { return Name() < other.Name(); } void AddFile(CountryFile const & file); - FilesContainerT const & Files() const { return m_files; } + + size_t GetFilesCount() const { return m_files.size(); } + + /* + template void ForEachFile(ToDo toDo) const + { + for (FilesContainerT::const_iterator i = m_files.begin(); i != m_files.end(); ++i) + toDo(i->GetFileWithExt()); + } + */ + + /// This function valid for current logic - one file for one country (region). + /// If the logic will be changed, replace GetFile with ForEachFile. + CountryFile const & GetFile() const + { + ASSERT_EQUAL ( m_files.size(), 1, () ); + return m_files.front(); + } string const & Name() const { return m_name; } string const & Flag() const { return m_flag; } int64_t Price() const; /// @return bounds for downloaded parts of the country or empty rect - m2::RectD Bounds() const; + //m2::RectD Bounds() const; LocalAndRemoteSizeT Size() const; }; diff --git a/storage/country_info.cpp b/storage/country_info.cpp index a475debf17..88fc5a7afd 100644 --- a/storage/country_info.cpp +++ b/storage/country_info.cpp @@ -13,12 +13,50 @@ namespace storage { + /* + class LessCountryDef + { + bool CompareStrings(string const & s1, string const & s2) const + { + // Do this stuff because of 'Guinea-Bissau.mwm' goes before 'Guinea.mwm' + // in file system (and in PACKED_POLYGONS_FILE also). + size_t n = min(s1.size(), s2.size()); + return lexicographical_compare(s1.begin(), s1.begin() + n, + s2.begin(), s2.begin() + n); + } + + public: + bool operator() (CountryDef const & r1, string const & r2) const + { + return CompareStrings(r1.m_name, r2); + } + bool operator() (string const & r1, CountryDef const & r2) const + { + return CompareStrings(r1, r2.m_name); + } + bool operator() (CountryDef const & r1, CountryDef const & r2) const + { + return CompareStrings(r1.m_name, r2.m_name); + } + }; + */ + CountryInfoGetter::CountryInfoGetter(ModelReaderPtr polyR, ModelReaderPtr countryR) : m_reader(polyR), m_cache(2) { ReaderSource src(m_reader.GetReader(PACKED_POLYGONS_INFO_TAG)); rw::Read(src, m_countries); +/* + // We can't change the order of countries. +#ifdef DEBUG + LessCountryDef check; + for (size_t i = 0; i < m_countries.size() - 1; ++i) + ASSERT ( !check(m_countries[i+1], m_countries[i]), + (m_countries[i].m_name, m_countries[i+1].m_name) ); +#endif +*/ + string buffer; countryR.ReadAsString(buffer); LoadCountryFile2CountryInfo(buffer, m_id2info); @@ -111,30 +149,60 @@ namespace storage CountryInfo::FileName2FullName(info.m_name); } + template void CountryInfoGetter::ForEachCountry(string const & prefix, ToDo toDo) const + { + typedef vector::const_iterator IterT; + + for (IterT i = m_countries.begin(); i != m_countries.end(); ++i) + { + if (i->m_name.find(prefix) != string::npos) + toDo(*i); + } + + /// @todo Store sorted list of polygons in PACKED_POLYGONS_FILE. + /* + pair r = equal_range(m_countries.begin(), m_countries.end(), file, LessCountryDef()); + while (r.first != r.second) + { + toDo(r.first->m_name); + ++r.first; + } + */ + } + + namespace + { + class DoCalcUSA + { + m2::RectD * m_rects; + public: + DoCalcUSA(m2::RectD * rects) : m_rects(rects) {} + void operator() (CountryDef const & c) + { + if (c.m_name == "USA_Alaska") + m_rects[1] = c.m_rect; + else if (c.m_name == "USA_Hawaii") + m_rects[2] = c.m_rect; + else + m_rects[0].Add(c.m_rect); + } + }; + + void AddRect(m2::RectD & r, CountryDef const & c) + { + r.Add(c.m_rect); + } + } + void CountryInfoGetter::CalcUSALimitRect(m2::RectD rects[3]) const { - for (size_t i = 0; i < m_countries.size(); ++i) - { - if (m_countries[i].m_name.find("USA_") == 0) - { - if (m_countries[i].m_name == "USA_Alaska") - rects[1] = m_countries[i].m_rect; - else if (m_countries[i].m_name == "USA_Hawaii") - rects[2] = m_countries[i].m_rect; - else - rects[0].Add(m_countries[i].m_rect); - } - } + ForEachCountry("USA_", DoCalcUSA(rects)); } m2::RectD CountryInfoGetter::CalcLimitRect(string const & prefix) const { m2::RectD r; - for (size_t i = 0; i < m_countries.size(); ++i) - { - if (m_countries[i].m_name.find(prefix) == 0) - r.Add(m_countries[i].m_rect); - } + ForEachCountry(prefix, bind(&AddRect, ref(r), _1)); return r; } } diff --git a/storage/country_info.hpp b/storage/country_info.hpp index 2525f1c9d2..5dcd95d970 100644 --- a/storage/country_info.hpp +++ b/storage/country_info.hpp @@ -16,6 +16,9 @@ namespace storage FilesContainerR m_reader; vector m_countries; + + template void ForEachCountry(string const & prefix, ToDo toDo) const; + /// ID - is a country file name without an extension. map m_id2info; diff --git a/storage/storage.cpp b/storage/storage.cpp index eff36a1870..8a734c32ea 100644 --- a/storage/storage.cpp +++ b/storage/storage.cpp @@ -69,11 +69,9 @@ namespace storage } //////////////////////////////////////////////////////////////////////////// - void Storage::Init(TAddMapFunction addFunc, TRemoveMapFunction removeFunc, TUpdateRectFunction updateRectFunc) + void Storage::Init(TUpdateAfterDownload const & updateFn) { - m_addMap = addFunc; - m_removeMap = removeFunc; - m_updateRect = updateRectFunc; + m_updateAfterDownload = updateFn; } CountriesContainerT const & NodeFromIndex(CountriesContainerT const & root, TIndex const & index) @@ -132,17 +130,7 @@ namespace storage if (m_failedCountries.count(index) > 0) return EDownloadFailed; - //if (m_indexGeneration.count(index) > 0) - // return EGeneratingIndex; - - LocalAndRemoteSizeT const size = CountryByIndex(index).Size(); - if (size.first == 0) - return ENotDownloaded; - - if (size.second == 0) - return EUnknown; - - return (size.first == size.second ? EOnDisk : EOnDiskOutOfDate); + return EUnknown; } void Storage::DownloadCountry(TIndex const & index) @@ -159,14 +147,10 @@ namespace storage m_failedCountries.erase(index); // add it into the queue m_queue.push_back(index); + // and start download if necessary if (m_queue.size() == 1) { - // reset total country's download progress - LocalAndRemoteSizeT const size = CountryByIndex(index).Size(); - m_countryProgress.first = 0; - m_countryProgress.second = size.second; - DownloadNextCountryFromQueue(); } else @@ -176,22 +160,6 @@ namespace storage } } - template - class DeactivateMap - { - string m_workingDir; - TRemoveFn & m_removeFn; - public: - DeactivateMap(TRemoveFn & removeFn) : m_removeFn(removeFn) - { - m_workingDir = GetPlatform().WritableDir(); - } - void operator()(CountryFile const & file) - { - m_removeFn(file.GetFileWithExt()); - } - }; - void Storage::NotifyStatusChanged(TIndex const & index) const { for (list::const_iterator it = m_observers.begin(); it != m_observers.end(); ++it) @@ -200,85 +168,37 @@ namespace storage void Storage::DownloadNextCountryFromQueue() { - while (!m_queue.empty()) + if (!m_queue.empty()) { - TIndex index = m_queue.front(); - FilesContainerT const & tiles = CountryByIndex(index).Files(); - for (FilesContainerT::const_iterator it = tiles.begin(); it != tiles.end(); ++it) - { - if (it->GetFileSize() == 0) - { - // send Country name for statistics - string const postBody = it->m_fileName; - m_request.reset(HttpRequest::PostJson(GetPlatform().MetaServerUrl(), - postBody, + TIndex const index = m_queue.front(); + Country const & country = CountryByIndex(index); + + /// Reset progress before downloading. + /// @todo If we will have more than one file per country, + /// we should initialize progress before calling DownloadNextCountryFromQueue(). + m_countryProgress.first = 0; + m_countryProgress.second = country.Size().second; + + // send Country name for statistics + m_request.reset(HttpRequest::PostJson(GetPlatform().MetaServerUrl(), + country.GetFile().m_fileName, bind(&Storage::OnServerListDownloaded, this, _1))); - // new status for country, "Downloading" - NotifyStatusChanged(index); - return; - } - } - - // continue with next country - m_queue.pop_front(); - - // reset total country's download progress - if (!m_queue.empty()) - { - m_countryProgress.first = 0; - m_countryProgress.second = CountryByIndex(m_queue.front()).Size().second; - } - - // new status for country, "OnDisk" + // new status for country, "Downloading" NotifyStatusChanged(index); } } - class DeleteMap - { - string m_workingDir; - public: - DeleteMap() - { - m_workingDir = GetPlatform().WritableDir(); - } - - void operator()(CountryFile const & file) - { - FileWriter::DeleteFileX(m_workingDir + file.m_fileName + DOWNLOADING_FILE_EXTENSION); - FileWriter::DeleteFileX(m_workingDir + file.m_fileName + RESUME_FILE_EXTENSION); - FileWriter::DeleteFileX(m_workingDir + file.GetFileWithExt()); - } - }; - - template - void DeactivateAndDeleteCountry(Country const & country, TRemoveFunc removeFunc) - { - // deactivate from multiindex - for_each(country.Files().begin(), country.Files().end(), DeactivateMap(removeFunc)); - // delete from disk - for_each(country.Files().begin(), country.Files().end(), DeleteMap()); - } - + /* m2::RectD Storage::CountryBounds(TIndex const & index) const { Country const & country = CountryByIndex(index); return country.Bounds(); } + */ - void Storage::DeleteCountryFiles(TIndex const & index) + bool Storage::DeleteFromDownloader(TIndex const & index) { - Country const & country = CountryByIndex(index); - for_each(country.Files().begin(), country.Files().end(), DeleteMap()); - } - - void Storage::DeleteCountry(TIndex const & index) - { - Country const & country = CountryByIndex(index); - - m2::RectD bounds; - // check if we already downloading this country TQueue::iterator found = find(m_queue.begin(), m_queue.end(), index); if (found != m_queue.end()) @@ -289,12 +209,6 @@ namespace storage m_request.reset(); // remove from the queue m_queue.erase(found); - // reset progress if the queue is not empty - if (!m_queue.empty()) - { - m_countryProgress.first = 0; - m_countryProgress.second = CountryByIndex(m_queue.front()).Size().second; - } // start another download if the queue is not empty DownloadNextCountryFromQueue(); } @@ -303,18 +217,11 @@ namespace storage // remove from the queue m_queue.erase(found); } - } - else - { - // bounds are only updated if country was already activated before - bounds = country.Bounds(); + + return true; } - DeactivateAndDeleteCountry(country, m_removeMap); - NotifyStatusChanged(index); - - if (bounds != m2::RectD::GetEmptyRect()) - m_updateRect(bounds); + return false; } void Storage::LoadCountriesFile(bool forceReload) @@ -332,8 +239,8 @@ namespace storage } } - int Storage::Subscribe(TChangeCountryFunction change, - TProgressFunction progress) + int Storage::Subscribe(TChangeCountryFunction const & change, + TProgressFunction const & progress) { CountryObservers obs; @@ -362,81 +269,32 @@ namespace storage { if (m_queue.empty()) { - ASSERT ( false, ("Invalid url?", request.Data()) ); + ASSERT ( false, ("queue can't be empty") ); return; } TIndex const index = m_queue.front(); + m_queue.pop_front(); + if (request.Status() == HttpRequest::EFailed) { - // remove failed country from the queue - m_queue.pop_front(); + // add to failed countries set m_failedCountries.insert(index); - - // notify GUI about failed country - NotifyStatusChanged(index); } else { - LocalAndRemoteSizeT const size = CountryByIndex(index).Size(); - if (size.second != 0) - m_countryProgress.first = size.first; + Country const & country = CountryByIndex(index); - // get file descriptor - string file = request.Data(); - pl::GetNameFromURLRequest(file); - - /// @todo By the way - this code os obsolete. - /// It doesn't supported properly in android now (because of Platform::RunOnGuiThread). - /* - Platform & pl = GetPlatform(); - if (pl.IsFeatureSupported("search")) - { - // Generate search index if it's supported in this build - m_indexGeneration.insert(index); - pl.RunAsync(bind(&Storage::GenerateSearchIndex, this, index, file)); - } - else - */ - { - // Or simply activate downloaded map - UpdateAfterSearchIndex(index, file); - } + // notify framework that downloading is done + m_updateAfterDownload(country.GetFile().GetFileWithExt()); } + NotifyStatusChanged(index); + m_request.reset(); DownloadNextCountryFromQueue(); } - /* - void Storage::GenerateSearchIndex(TIndex const & index, string const & fName) - { - if (indexer::BuildSearchIndexFromDatFile(fName)) - { - GetPlatform().RunOnGuiThread(bind(&Storage::UpdateAfterSearchIndex, this, index, fName)); - } - else - { - LOG(LERROR, ("Can't build search index for", fName)); - } - } - */ - - void Storage::UpdateAfterSearchIndex(TIndex const & index, string const & fName) - { - // remove from index set - //m_indexGeneration.erase(index); - NotifyStatusChanged(index); - - // activate downloaded map piece - m_addMap(fName); - - // update rect from downloaded file - feature::DataHeader header; - LoadMapHeader(GetPlatform().GetReader(fName), header); - m_updateRect(header.GetBounds()); - } - void Storage::ReportProgress(TIndex const & idx, pair const & p) { for (ObserversContT::const_iterator i = m_observers.begin(); i != m_observers.end(); ++i) @@ -447,7 +305,7 @@ namespace storage { if (m_queue.empty()) { - ASSERT(false, ("queue can't be empty")); + ASSERT ( false, ("queue can't be empty") ); return; } @@ -465,12 +323,11 @@ namespace storage { if (m_queue.empty()) { - ASSERT(false, ("this should never happen")); + ASSERT ( false, ("queue can't be empty") ); return; } - // @TODO now supports only one file in the country - CountryFile const & file = CountryByIndex(m_queue.front()).Files().front(); + CountryFile const & file = CountryByIndex(m_queue.front()).GetFile(); vector urls; GetServerListFromRequest(request, urls); @@ -479,9 +336,8 @@ namespace storage for (size_t i = 0; i < urls.size(); ++i) urls[i] = GetFileDownloadUrl(urls[i], file.GetFileWithExt()); - m_request.reset(HttpRequest::GetFile(urls, - GetPlatform().WritablePathForFile(file.GetFileWithExt()), - file.m_remoteSize, + string const fileName = GetPlatform().WritablePathForFile(file.GetFileWithExt() + READY_FILE_EXTENSION); + m_request.reset(HttpRequest::GetFile(urls, fileName, file.m_remoteSize, bind(&Storage::OnMapDownloadFinished, this, _1), bind(&Storage::OnMapDownloadProgress, this, _1))); ASSERT ( m_request, () ); diff --git a/storage/storage.hpp b/storage/storage.hpp index ae0f8bc51a..3eabcfe2f7 100644 --- a/storage/storage.hpp +++ b/storage/storage.hpp @@ -12,6 +12,7 @@ #include "../std/function.hpp" #include "../std/scoped_ptr.hpp" + namespace storage { /// Used in GUI @@ -23,7 +24,6 @@ namespace storage EDownloading, EInQueue, EUnknown, - EGeneratingIndex, EOnDiskOutOfDate }; @@ -81,9 +81,6 @@ namespace storage typedef set TCountriesSet; TCountriesSet m_failedCountries; - /// store countries set for which search index is generating - //TCountriesSet m_indexGeneration; - /// used to correctly calculate total country download progress with more than 1 file /// downloader::HttpRequest::ProgressT m_countryProgress; @@ -108,24 +105,11 @@ namespace storage /// @name Communicate with Framework //@{ - typedef vector map_list_t; - public: - typedef function TAddMapFunction; - typedef function TRemoveMapFunction; - typedef function TUpdateRectFunction; - typedef function TEnumMapsFunction; - - private: - TAddMapFunction m_addMap; - TRemoveMapFunction m_removeMap; - TUpdateRectFunction m_updateRect; + typedef function TUpdateAfterDownload; + TUpdateAfterDownload m_updateAfterDownload; //@} void DownloadNextCountryFromQueue(); - Country const & CountryByIndex(TIndex const & index) const; - - //void GenerateSearchIndex(TIndex const & index, string const & fName); - void UpdateAfterSearchIndex(TIndex const & index, string const & fName); void LoadCountriesFile(bool forceReload); @@ -134,9 +118,7 @@ namespace storage public: Storage(); - void Init(TAddMapFunction addFunc, - TRemoveMapFunction removeFunc, - TUpdateRectFunction updateRectFunc); + void Init(TUpdateAfterDownload const & updateFn); /// @name Called from DownloadManager //@{ @@ -149,11 +131,12 @@ namespace storage //@{ /// @return unique identifier that should be used with Unsubscribe function - int Subscribe(TChangeCountryFunction change, - TProgressFunction progress); + int Subscribe(TChangeCountryFunction const & change, + TProgressFunction const & progress); void Unsubscribe(int slotId); //@} + Country const & CountryByIndex(TIndex const & index) const; TIndex const FindIndexByName(string const & name) const; size_t CountriesCount(TIndex const & index) const; @@ -161,11 +144,10 @@ namespace storage string const & CountryFlag(TIndex const & index) const; LocalAndRemoteSizeT CountrySizeInBytes(TIndex const & index) const; TStatus CountryStatus(TIndex const & index) const; - m2::RectD CountryBounds(TIndex const & index) const; + //m2::RectD CountryBounds(TIndex const & index) const; void DownloadCountry(TIndex const & index); - void DeleteCountry(TIndex const & index); - void DeleteCountryFiles(TIndex const & index); + bool DeleteFromDownloader(TIndex const & index); void CheckForUpdate();