26
26
#include < QtCore/QtDebug>
27
27
28
28
#include < fmt/format.h>
29
+ #include < ios>
29
30
#include < iterator>
31
+ #include < type_traits>
30
32
31
33
namespace Mayo {
32
34
33
35
namespace {
34
36
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)
36
85
{
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
+ }
41
93
42
- recentFile->filepath = filepathFrom (strFilepath);
43
- stream >> recentFile->thumbnail .imageData ;
44
- if (stream.status () != QDataStream::Ok)
45
- return ;
94
+ return true ;
95
+ }
46
96
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
+ }
54
174
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;
56
186
}
57
187
58
188
QuantityLength shapeChordalDeflection (const TopoDS_Shape& shape)
@@ -162,12 +292,11 @@ Settings::Variant AppModule::toVariant(const Property& prop) const
162
292
{
163
293
if (isType<PropertyRecentFiles>(prop)) {
164
294
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 ());
171
300
}
172
301
else {
173
302
return PropertyValueConversion::toVariant (prop);
@@ -176,17 +305,20 @@ Settings::Variant AppModule::toVariant(const Property& prop) const
176
305
177
306
bool AppModule::fromVariant (Property* prop, const Settings::Variant& variant) const
178
307
{
308
+ bool ok = false ;
179
309
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);
184
311
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);
186
316
}
187
317
else {
188
- return PropertyValueConversion::fromVariant (prop, variant);
318
+ ok = PropertyValueConversion::fromVariant (prop, variant);
189
319
}
320
+
321
+ return ok;
190
322
}
191
323
192
324
void AppModule::emitMessage (MessageType msgType, std::string_view text)
@@ -210,38 +342,17 @@ void AppModule::clearMessageLog()
210
342
this ->signalMessageLogCleared .send ();
211
343
}
212
344
213
- void AppModule::prependRecentFile (const FilePath& fp)
345
+ void AppModule::prependRecentFile (const FilePath& fp, GuiDocument* guiDoc )
214
346
{
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);
232
351
}
233
352
234
353
const RecentFile* AppModule::findRecentFile (const FilePath& fp) const
235
354
{
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 ());
245
356
}
246
357
247
358
void AppModule::recordRecentFile (GuiDocument* guiDoc)
@@ -303,45 +414,80 @@ void AppModule::setRecentFileThumbnailRecorder(std::function<Thumbnail(GuiDocume
303
414
m_fnRecentFileThumbnailRecorder = std::move (fn);
304
415
}
305
416
306
-
307
- void AppModule::readRecentFiles (QDataStream& stream, RecentFiles* recentFiles)
417
+ void AppModule::read (QDataStream& stream, RecentFiles* recentFiles)
308
418
{
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));
316
433
}
434
+ }
435
+ catch (const std::exception&) {
436
+ }
437
+ }
317
438
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
+ }
320
448
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
+ }
325
459
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
+ }
332
464
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&) {
335
481
}
336
482
}
337
483
338
- void AppModule::writeRecentFiles (QDataStream& stream, const RecentFiles& recentFiles )
484
+ void AppModule::write (QDataStream& stream, const RecentScripts& recentScripts )
339
485
{
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 );
345
491
}
346
492
}
347
493
0 commit comments