You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
371 lines
7.7 KiB
C++
371 lines
7.7 KiB
C++
#include "stdafx.h"
|
|
#include "BlobSerializer.h"
|
|
#include "ClassFactory.h"
|
|
#include "DrawOperator/XySerializerHelper.h"
|
|
#include "SigmaDoc.h"
|
|
#include "SigmaStore.h"
|
|
#include "UndoManager/UndoManager.h"
|
|
#include "Util.h"
|
|
#include "../DrawOperator/Util.h"
|
|
#include "StorageFactory.h"
|
|
#include "AsyncWorker.h"
|
|
#include <chrono>
|
|
#include <filesystem>
|
|
#include <optional>
|
|
#include <mutex>
|
|
|
|
/**
|
|
* @brief CSigmaStore::Impl 类的实现
|
|
*/
|
|
class CSigmaStore::Impl
|
|
{
|
|
public:
|
|
Impl(std::shared_ptr<IActionStorage> storage)
|
|
: m_storage(storage)
|
|
{
|
|
}
|
|
|
|
void SetActions(CSigmaDoc* pDoc)
|
|
{
|
|
const std::deque<CAction*>& redoStack = pDoc->GetActionManager()->GetRedoStack();
|
|
|
|
std::vector<ActionRecord> records = m_asyncWorker.postTask([=]()
|
|
{
|
|
return m_storage->RetrieveAllActionRecords(CStringToUtf8String(pDoc->m_FileName));
|
|
}).get();
|
|
|
|
for (const auto& record : records)
|
|
{
|
|
CAction* pAction = DeserializeAction(pDoc, record.data);
|
|
if (pAction != nullptr)
|
|
{
|
|
if (CActionItem* pActionItem = dynamic_cast<CActionItem*>(pAction))
|
|
{
|
|
const_cast<std::deque<CAction*>&>(redoStack).push_front(pAction);
|
|
pDoc->Redo();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 保存操作记录
|
|
* @param actions 操作记录向量
|
|
* @param path 文件路径
|
|
*/
|
|
void SaveActions(std::vector<ActionRecord>& actions, const CString& path)
|
|
{
|
|
if (!m_storage)
|
|
{
|
|
throw std::runtime_error("m_storage is nullptr");
|
|
}
|
|
|
|
if (actions.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::string filePath = CStringToUtf8String(path);
|
|
|
|
CacheActions(path, actions);
|
|
|
|
m_asyncWorker.postTask([this, storage = m_storage, filePath, movedActions = std::move(actions)]()
|
|
{
|
|
for (auto& record : movedActions)
|
|
{
|
|
if (!storage->ExistsActionRecord(filePath, record.uuid))
|
|
{
|
|
storage->AddActionRecord(filePath, record);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @brief 根据需要保存文件数据
|
|
* @param pDoc 文档指针
|
|
* @param path 文件路径
|
|
*/
|
|
void SaveFileDataIfNeed(CSigmaDoc* pDoc, const CString& path)
|
|
{
|
|
if (!m_storage)
|
|
{
|
|
throw std::runtime_error("m_storage is nullptr");
|
|
}
|
|
|
|
std::string filePath = CStringToUtf8String(path);
|
|
if (!m_storage->ExistsFileData(filePath))
|
|
{
|
|
auto rawData = CXySerializerHelper::SerializeCXy(pDoc->m_pXy);
|
|
auto pFileData = std::make_shared<FileData>(
|
|
-1, filePath, reinterpret_cast<std::vector<char>&>(rawData), FormatTime(std::chrono::system_clock::now())
|
|
);
|
|
|
|
m_asyncWorker.postTask([this, filePath, pFileData]()
|
|
{
|
|
m_storage->InsertFileData(filePath, *pFileData);
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 获取文档的操作记录
|
|
* @param pDoc 文档指针
|
|
* @return 操作记录向量
|
|
*/
|
|
std::vector<ActionRecord> GetActionRecords(const CSigmaDoc* pDoc)
|
|
{
|
|
if (!m_storage)
|
|
{
|
|
throw std::runtime_error("m_storage is nullptr");
|
|
}
|
|
|
|
std::vector<ActionRecord> result;
|
|
auto actionManager = const_cast<CSigmaDoc*>(pDoc)->GetActionManager();
|
|
|
|
if (actionManager)
|
|
{
|
|
std::vector<CAction*> actions;
|
|
const auto& undoStack = actionManager->GetUndoStack();
|
|
for (auto* action : undoStack)
|
|
{
|
|
actions.push_back(action);
|
|
}
|
|
|
|
const auto& redoStack = actionManager->GetRedoStack();
|
|
for (auto it = redoStack.rbegin(); it != redoStack.rend(); ++it)
|
|
{
|
|
actions.push_back(*it);
|
|
}
|
|
|
|
CSplitPath sp("");
|
|
CString path = sp.GetAbsolutePath(pDoc->m_FileName);
|
|
|
|
for (auto* action : actions)
|
|
{
|
|
if (InCache(path, action->GetUUID()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
result.push_back({
|
|
-1,
|
|
action->GetUUID(),
|
|
action->GetActionName(),
|
|
"",
|
|
SerializeAction(action),
|
|
FormatTime(action->GetTimePoint()),
|
|
FormatTime(std::chrono::system_clock::now())
|
|
});
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief 获取操作记录数量
|
|
* @param path 文件路径
|
|
* @return 操作记录数量
|
|
*/
|
|
int ActionRecordCount(const CString& path)
|
|
{
|
|
if (!m_storage)
|
|
{
|
|
throw std::runtime_error("m_storage is nullptr");
|
|
}
|
|
|
|
return m_asyncWorker.postTask([=]()
|
|
{
|
|
std::string u8path = CStringToUtf8String(path);
|
|
return m_storage->ActionRecordCount(u8path);
|
|
}).get();
|
|
}
|
|
|
|
/**
|
|
* @brief 检索文件数据
|
|
* @param filePath 文件路径
|
|
* @return 文件数据(可选)
|
|
*/
|
|
std::optional<FileData> RetrieveFileData(const CString& filePath)
|
|
{
|
|
if (!m_storage)
|
|
{
|
|
throw std::runtime_error("m_storage is nullptr");
|
|
}
|
|
|
|
return m_asyncWorker.postTask([=]()
|
|
{
|
|
std::string u8path = CStringToUtf8String(filePath);
|
|
return m_storage->RetrieveFileData(u8path);
|
|
}).get();
|
|
}
|
|
|
|
/**
|
|
* @brief 清除指定路径的备份
|
|
* @param path 文件路径
|
|
*/
|
|
void Clear(const CString& path)
|
|
{
|
|
if (!m_storage)
|
|
{
|
|
throw std::runtime_error("m_storage is nullptr");
|
|
}
|
|
|
|
m_asyncWorker.postTask([=]()
|
|
{
|
|
std::string u8path = CStringToUtf8String(path);
|
|
m_storage->ClearBackup(u8path);
|
|
}).wait();
|
|
|
|
ClearActionCache(path);
|
|
}
|
|
|
|
private:
|
|
void ClearActionCache(const CString& path)
|
|
{
|
|
auto it = m_actionCache.find(path);
|
|
if (it != m_actionCache.end())
|
|
{
|
|
m_actionCache.erase(it);
|
|
}
|
|
}
|
|
|
|
void CacheActions(const CString& path, const std::vector<ActionRecord>& actions)
|
|
{
|
|
std::set<std::string> set;
|
|
for (const ActionRecord& action : actions)
|
|
{
|
|
set.insert(action.uuid);
|
|
}
|
|
m_actionCache[path] = std::move(set);
|
|
}
|
|
|
|
bool InCache(const CString& path, const std::string& uuid) const
|
|
{
|
|
auto it = m_actionCache.find(path);
|
|
if (it == m_actionCache.end())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return it->second.find(uuid) != it->second.end();
|
|
}
|
|
|
|
std::shared_ptr<IActionStorage> m_storage;
|
|
AsyncWorker m_asyncWorker;
|
|
std::unordered_map<CString, std::set<std::string>, CStringHash> m_actionCache;
|
|
};
|
|
|
|
|
|
CSigmaStore::CSigmaStore(std::shared_ptr<IActionStorage> storage)
|
|
: m_impl(std::make_unique<CSigmaStore::Impl>(storage))
|
|
{
|
|
}
|
|
|
|
CSigmaStore::~CSigmaStore()
|
|
{
|
|
}
|
|
|
|
bool CSigmaStore::Exist(const CString& path)
|
|
{
|
|
try
|
|
{
|
|
return m_impl->ActionRecordCount(path) > 0;
|
|
}
|
|
catch (std::runtime_error& e)
|
|
{
|
|
TRACE("Exist %s failed: %s\n", path, e.what());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CSigmaStore::Load(CSigmaDoc* pDoc, const CString& path)
|
|
{
|
|
try
|
|
{
|
|
auto fileDataOpt = m_impl->RetrieveFileData(path);
|
|
if (!fileDataOpt.has_value())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
auto& fileData = fileDataOpt.value();
|
|
|
|
pDoc->m_pXy = CXySerializerHelper::DeserializeCXy(reinterpret_cast<std::vector<BYTE>&>(fileData.data)).release();
|
|
pDoc->m_FileName = Utf8StringToCString(fileData.filepath);
|
|
m_impl->SetActions(pDoc);
|
|
|
|
return pDoc;
|
|
}
|
|
catch (const std::runtime_error &e)
|
|
{
|
|
TRACE("Load %s failed: %s\n", path, e.what());
|
|
return false;
|
|
}
|
|
catch (CArchiveException *e)
|
|
{
|
|
return false;
|
|
}
|
|
catch (CException* e)
|
|
{
|
|
TCHAR errorMessage[512]{ 0 };
|
|
UINT nHelpContext = 0;
|
|
|
|
e->GetErrorMessage(errorMessage, sizeof(errorMessage), &nHelpContext);
|
|
TRACE("Load %s failed: %s\n", path, errorMessage);
|
|
}
|
|
catch (...)
|
|
{
|
|
TRACE("Load %s failed: Unkown");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CSigmaStore::Save(CSigmaDoc* pDoc)
|
|
{
|
|
CSplitPath sp("");
|
|
CString path = sp.GetAbsolutePath(pDoc->m_FileName);
|
|
|
|
try
|
|
{
|
|
m_impl->SaveFileDataIfNeed(pDoc, path);
|
|
m_impl->SaveActions(m_impl->GetActionRecords(pDoc), path);
|
|
|
|
return true;
|
|
}
|
|
catch (const std::runtime_error &e)
|
|
{
|
|
TRACE("Save %s failed: %s\n", pDoc->m_FileName, e.what());
|
|
return false;
|
|
}
|
|
catch (CException* e)
|
|
{
|
|
TCHAR errorMessage[512]{ 0 };
|
|
UINT nHelpContext = 0;
|
|
|
|
e->GetErrorMessage(errorMessage, sizeof(errorMessage), &nHelpContext);
|
|
TRACE("Save %s failed: %s\n", path, errorMessage);
|
|
}
|
|
catch (...)
|
|
{
|
|
TRACE("Save %s failed: Unkown");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void CSigmaStore::Clear(const CString& path)
|
|
{
|
|
try
|
|
{
|
|
m_impl->Clear(path);
|
|
}
|
|
catch (const std::runtime_error& e)
|
|
{
|
|
TRACE("Clear %s failed: %s\n", path, e.what());
|
|
}
|
|
}
|