Skip to content

Commit 83e3b34

Browse files
committed
WIP8
1 parent 9c705e4 commit 83e3b34

17 files changed

+476
-144
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,7 @@ list(
637637
${PROJECT_SOURCE_DIR}/src/app/app_module.h
638638
${PROJECT_SOURCE_DIR}/src/app/library_info.h
639639
${PROJECT_SOURCE_DIR}/src/app/recent_files.h
640+
${PROJECT_SOURCE_DIR}/src/app/recent_scripts.h
640641
)
641642

642643
file(
@@ -651,6 +652,7 @@ list(
651652
${PROJECT_SOURCE_DIR}/src/app/app_module.cpp
652653
${PROJECT_SOURCE_DIR}/src/app/library_info.cpp
653654
${PROJECT_SOURCE_DIR}/src/app/recent_files.cpp
655+
${PROJECT_SOURCE_DIR}/src/app/recent_scripts.cpp
654656
)
655657

656658
if(Mayo_BuildConvCli)

src/app/app_module.cpp

Lines changed: 231 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -26,33 +26,163 @@
2626
#include <QtCore/QtDebug>
2727

2828
#include <fmt/format.h>
29+
#include <ios>
2930
#include <iterator>
31+
#include <type_traits>
3032

3133
namespace Mayo {
3234

3335
namespace {
3436

35-
void readRecentFile(QDataStream& stream, RecentFile* recentFile)
37+
template<typename T>
38+
struct Serializer {
39+
static auto fnType()
40+
{
41+
if constexpr(std::is_same_v<T, int>)
42+
return qint32{};
43+
else if constexpr(std::is_same_v<T, unsigned>)
44+
return quint32{};
45+
else if constexpr(std::is_same_v<T, std::int64_t>)
46+
return qint64{};
47+
else if constexpr(std::is_same_v<T, std::uint64_t>)
48+
return quint64{};
49+
else if constexpr(std::is_same_v<T, std::time_t>)
50+
return qint64{};
51+
else if constexpr(std::is_same_v<T, FilePath>)
52+
return QString{};
53+
else
54+
return T{};
55+
}
56+
57+
using StreamType = decltype(Serializer<T>::fnType());
58+
59+
static void read(QDataStream& stream, T& v)
60+
{
61+
if constexpr(std::is_same_v<T, FilePath>) {
62+
QString strFilePath;
63+
stream >> strFilePath;
64+
v = filepathFrom(strFilePath);
65+
}
66+
else {
67+
StreamType qv;
68+
stream >> qv;
69+
v = static_cast<T>(qv);
70+
}
71+
}
72+
73+
static void write(QDataStream& stream, const T& v)
74+
{
75+
if constexpr(std::is_same_v<T, FilePath>) {
76+
stream << filepathTo<QString>(v);
77+
}
78+
else {
79+
stream << static_cast<StreamType>(v);
80+
}
81+
}
82+
};
83+
84+
bool checkDataStreamStatus(QDataStream::Status status)
3685
{
37-
QString strFilepath;
38-
stream >> strFilepath;
39-
if (stream.status() != QDataStream::Ok)
40-
return;
86+
if (status != QDataStream::Ok) {
87+
qDebug() << fmt::format(
88+
"QDataStream error\n Function: {}\n Status: {}",
89+
Q_FUNC_INFO, MetaEnum::name(status)
90+
).c_str();
91+
return false;
92+
}
4193

42-
recentFile->filepath = filepathFrom(strFilepath);
43-
stream >> recentFile->thumbnail.imageData;
44-
if (stream.status() != QDataStream::Ok)
45-
return;
94+
return true;
95+
}
4696

47-
recentFile->thumbnail.imageCacheKey = -1;
48-
// Read thumbnail timestamp
49-
// Warning: qint64 and int64_t may not be the exact same type(eg __int64 and longlong with Windows/MSVC)
50-
qint64 timestamp;
51-
stream >> timestamp;
52-
if (stream.status() != QDataStream::Ok)
53-
return;
97+
template<typename T>
98+
void dataStreamRead(QDataStream& stream, T& v)
99+
{
100+
Serializer<T>::read(stream, v);
101+
if (!checkDataStreamStatus(stream.status()))
102+
throw std::ios_base::failure("serialize() error with QDataStream");
103+
}
104+
105+
template<typename T>
106+
void dataStreamWrite(QDataStream& stream, T v)
107+
{
108+
Serializer<T>::write(stream, v);
109+
}
110+
111+
template<typename T> void check_has_filepath_attribute() {
112+
static_assert(
113+
std::is_same_v<decltype(T::filepath), FilePath>,
114+
"Type 'T' must have 'filepath' member attribute of type 'std::filesystem::path'"
115+
);
116+
}
117+
118+
template<typename RecentItem>
119+
const RecentItem* findRecentItem(const FilePath& fp, const std::vector<RecentItem>& listRecentItem)
120+
{
121+
check_has_filepath_attribute<RecentItem>();
122+
auto itFound =
123+
std::find_if(
124+
listRecentItem.cbegin(),
125+
listRecentItem.cend(),
126+
[=](const RecentItem& recentItem) { return filepathEquivalent(fp, recentItem.filepath); }
127+
);
128+
return itFound != listRecentItem.cend() ? &(*itFound) : nullptr;
129+
}
130+
131+
template<typename RecentItem>
132+
void prependRecentItem(
133+
const FilePath& fp,
134+
GenericProperty<std::vector<RecentItem>>& propRecentItems,
135+
std::function<void(RecentItem&)> fnUpdateRecentItem = nullptr
136+
)
137+
{
138+
using RecentItems = std::vector<RecentItem>;
139+
const RecentItem* ptrRecentItem = findRecentItem(fp, propRecentItems.value());
140+
RecentItems newRecentItems = propRecentItems.value();
141+
if (ptrRecentItem) {
142+
RecentItem& firstRecentItem = newRecentItems.front();
143+
RecentItem& recentItem = newRecentItems.at(ptrRecentItem - &propRecentItems.value().front());
144+
if (fnUpdateRecentItem)
145+
fnUpdateRecentItem(recentItem);
146+
147+
std::swap(firstRecentItem, recentItem);
148+
}
149+
else {
150+
RecentItem recentItem;
151+
recentItem.filepath = fp;
152+
if (fnUpdateRecentItem)
153+
fnUpdateRecentItem(recentItem);
154+
155+
newRecentItems.insert(newRecentItems.begin(), std::move(recentItem));
156+
constexpr unsigned sizeLimit = 15;
157+
while (newRecentItems.size() > sizeLimit)
158+
newRecentItems.pop_back();
159+
}
160+
161+
propRecentItems.setValue(newRecentItems);
162+
}
163+
164+
template<typename RecentItem>
165+
Settings::Variant recentItemsToVariant(const std::vector<RecentItem>& recentItems)
166+
{
167+
QByteArray blob;
168+
QDataStream stream(&blob, QIODevice::WriteOnly);
169+
AppModule::write(stream, recentItems);
170+
Settings::Variant varBlob(blob.toStdString());
171+
varBlob.setByteArray(true);
172+
return varBlob;
173+
}
54174

55-
recentFile->thumbnailTimestamp = timestamp;
175+
template<typename RecentItem>
176+
std::vector<RecentItem> recentItemsFromVariant(const Settings::Variant& variant, bool* ok = nullptr)
177+
{
178+
const QByteArray blob = QtCoreUtils::QByteArray_frowRawData(variant.toConstRefString());
179+
QDataStream stream(blob);
180+
std::vector<RecentItem> recentItems;
181+
AppModule::read(stream, &recentItems);
182+
if (ok)
183+
*ok = stream.status() == QDataStream::Ok;
184+
185+
return recentItems;
56186
}
57187

58188
QuantityLength shapeChordalDeflection(const TopoDS_Shape& shape)
@@ -162,12 +292,11 @@ Settings::Variant AppModule::toVariant(const Property& prop) const
162292
{
163293
if (isType<PropertyRecentFiles>(prop)) {
164294
const auto& filesProp = constRef<PropertyRecentFiles>(prop);
165-
QByteArray blob;
166-
QDataStream stream(&blob, QIODevice::WriteOnly);
167-
AppModule::writeRecentFiles(stream, filesProp.value());
168-
Variant varBlob(blob.toStdString());
169-
varBlob.setByteArray(true);
170-
return varBlob;
295+
return recentItemsToVariant(filesProp.value());
296+
}
297+
else if (isType<PropertyRecentScripts>(prop)) {
298+
const auto& scriptsProp = constRef<PropertyRecentScripts>(prop);
299+
return recentItemsToVariant(scriptsProp.value());
171300
}
172301
else {
173302
return PropertyValueConversion::toVariant(prop);
@@ -176,17 +305,20 @@ Settings::Variant AppModule::toVariant(const Property& prop) const
176305

177306
bool AppModule::fromVariant(Property* prop, const Settings::Variant& variant) const
178307
{
308+
bool ok = false;
179309
if (isType<PropertyRecentFiles>(prop)) {
180-
const QByteArray blob = QtCoreUtils::QByteArray_frowRawData(variant.toConstRefString());
181-
QDataStream stream(blob);
182-
RecentFiles recentFiles;
183-
AppModule::readRecentFiles(stream, &recentFiles);
310+
auto recentFiles = recentItemsFromVariant<RecentFile>(variant, &ok);
184311
ptr<PropertyRecentFiles>(prop)->setValue(recentFiles);
185-
return stream.status() == QDataStream::Ok;
312+
}
313+
else if (isType<PropertyRecentScripts>(prop)) {
314+
auto recentScripts = recentItemsFromVariant<RecentScript>(variant, &ok);
315+
ptr<PropertyRecentScripts>(prop)->setValue(recentScripts);
186316
}
187317
else {
188-
return PropertyValueConversion::fromVariant(prop, variant);
318+
ok = PropertyValueConversion::fromVariant(prop, variant);
189319
}
320+
321+
return ok;
190322
}
191323

192324
void AppModule::emitMessage(MessageType msgType, std::string_view text)
@@ -210,38 +342,17 @@ void AppModule::clearMessageLog()
210342
this->signalMessageLogCleared.send();
211343
}
212344

213-
void AppModule::prependRecentFile(const FilePath& fp)
345+
void AppModule::prependRecentFile(const FilePath& fp, GuiDocument* guiDoc)
214346
{
215-
const RecentFile* ptrRecentFile = this->findRecentFile(fp);
216-
RecentFiles newRecentFiles = m_props.recentFiles.value();
217-
if (ptrRecentFile) {
218-
RecentFile& firstRecentFile = newRecentFiles.front();
219-
RecentFile& recentFile = newRecentFiles.at(ptrRecentFile - &m_props.recentFiles.value().front());
220-
std::swap(firstRecentFile, recentFile);
221-
}
222-
else {
223-
RecentFile recentFile;
224-
recentFile.filepath = fp;
225-
newRecentFiles.insert(newRecentFiles.begin(), std::move(recentFile));
226-
constexpr int sizeLimit = 15;
227-
while (newRecentFiles.size() > sizeLimit)
228-
newRecentFiles.pop_back();
229-
}
230-
231-
m_props.recentFiles.setValue(newRecentFiles);
347+
auto fnUpdate = [=](RecentFile& recent) {
348+
this->impl_recordRecentFile(&recent, guiDoc);
349+
};
350+
prependRecentItem<RecentFile>(fp, m_props.recentFiles, fnUpdate);
232351
}
233352

234353
const RecentFile* AppModule::findRecentFile(const FilePath& fp) const
235354
{
236-
const RecentFiles& listRecentFile = m_props.recentFiles.value();
237-
auto itFound =
238-
std::find_if(
239-
listRecentFile.cbegin(),
240-
listRecentFile.cend(),
241-
[=](const RecentFile& recentFile) {
242-
return filepathEquivalent(fp, recentFile.filepath);
243-
});
244-
return itFound != listRecentFile.cend() ? &(*itFound) : nullptr;
355+
return findRecentItem(fp, m_props.recentFiles.value());
245356
}
246357

247358
void AppModule::recordRecentFile(GuiDocument* guiDoc)
@@ -303,45 +414,80 @@ void AppModule::setRecentFileThumbnailRecorder(std::function<Thumbnail(GuiDocume
303414
m_fnRecentFileThumbnailRecorder = std::move(fn);
304415
}
305416

306-
307-
void AppModule::readRecentFiles(QDataStream& stream, RecentFiles* recentFiles)
417+
void AppModule::read(QDataStream& stream, RecentFiles* recentFiles)
308418
{
309-
auto fnCheckStreamStatus = [](QDataStream::Status status) {
310-
if (status != QDataStream::Ok) {
311-
qDebug() << fmt::format(
312-
"QDataStream error\n Function: {}\n Status: {}",
313-
Q_FUNC_INFO, MetaEnum::name(status)
314-
).c_str();
315-
return false;
419+
try {
420+
unsigned count = 0;
421+
dataStreamRead(stream, count);
422+
recentFiles->clear();
423+
for (unsigned i = 0; i < count; ++i) {
424+
RecentFile recent;
425+
dataStreamRead(stream, recent.filepath);
426+
dataStreamRead(stream, recent.thumbnail.imageData);
427+
recent.thumbnail.imageCacheKey = -1;
428+
// Read thumbnail timestamp
429+
// Warning: qint64 and int64_t may not be the exact same type(eg __int64 and longlong with Windows/MSVC)
430+
dataStreamRead(stream, recent.thumbnailTimestamp);
431+
if (!recent.filepath.empty() && recent.thumbnailTimestamp != 0)
432+
recentFiles->push_back(std::move(recent));
316433
}
434+
}
435+
catch (const std::exception&) {
436+
}
437+
}
317438

318-
return true;
319-
};
439+
void AppModule::write(QDataStream& stream, const RecentFiles& recentFiles)
440+
{
441+
dataStreamWrite(stream, unsigned(recentFiles.size()));
442+
for (const RecentFile& rf : recentFiles) {
443+
dataStreamWrite(stream, rf.filepath);
444+
dataStreamWrite(stream, rf.thumbnail.imageData);
445+
dataStreamWrite(stream, rf.thumbnailTimestamp);
446+
}
447+
}
320448

321-
uint32_t count = 0;
322-
stream >> count;
323-
if (!fnCheckStreamStatus(stream.status()))
324-
return; // Stream extraction error, abort
449+
void AppModule::prependRecentScript(const FilePath& fp)
450+
{
451+
auto fnUpdate = [](RecentScript& recent) {
452+
++recent.executionCount;
453+
std::timespec ts;
454+
std::timespec_get(&ts, TIME_UTC);
455+
recent.lastExecutionDateTime = ts.tv_sec;
456+
};
457+
prependRecentItem<RecentScript>(fp, m_props.recentScripts, fnUpdate);
458+
}
325459

326-
recentFiles->clear();
327-
for (uint32_t i = 0; i < count; ++i) {
328-
RecentFile recent;
329-
readRecentFile(stream, &recent);
330-
if (!fnCheckStreamStatus(stream.status()))
331-
return; // Stream extraction error, abort
460+
const RecentScript* AppModule::findRecentScript(const FilePath& fp) const
461+
{
462+
return findRecentItem(fp, m_props.recentScripts.value());
463+
}
332464

333-
if (!recent.filepath.empty() && recent.thumbnailTimestamp != 0)
334-
recentFiles->push_back(std::move(recent));
465+
void AppModule::read(QDataStream& stream, RecentScripts* recentScripts)
466+
{
467+
try {
468+
unsigned count = 0;
469+
dataStreamRead(stream, count);
470+
recentScripts->clear();
471+
for (unsigned i = 0; i < count; ++i) {
472+
RecentScript recent;
473+
dataStreamRead(stream, recent.filepath);
474+
dataStreamRead(stream, recent.executionCount);
475+
dataStreamRead(stream, recent.lastExecutionDateTime);
476+
if (!recent.filepath.empty())
477+
recentScripts->push_back(std::move(recent));
478+
}
479+
}
480+
catch (const std::exception&) {
335481
}
336482
}
337483

338-
void AppModule::writeRecentFiles(QDataStream& stream, const RecentFiles& recentFiles)
484+
void AppModule::write(QDataStream& stream, const RecentScripts& recentScripts)
339485
{
340-
stream << uint32_t(recentFiles.size());
341-
for (const RecentFile& rf : recentFiles) {
342-
stream << filepathTo<QString>(rf.filepath);
343-
stream << rf.thumbnail.imageData;
344-
stream << qint64(rf.thumbnailTimestamp);
486+
dataStreamWrite(stream, unsigned(recentScripts.size()));
487+
for (const RecentScript& rs : recentScripts) {
488+
dataStreamWrite(stream, rs.filepath);
489+
dataStreamWrite(stream, rs.executionCount);
490+
dataStreamWrite(stream, rs.lastExecutionDateTime);
345491
}
346492
}
347493

0 commit comments

Comments
 (0)