ORE Studio 0.0.4
Loading...
Searching...
No Matches
ImageCache.hpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * Copyright (C) 2025 Marco Craveiro <marco.craveiro@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation; either version 3 of the License, or (at your option) any later
8 * version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 * details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 51
17 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 */
20#ifndef ORES_QT_IMAGE_CACHE_HPP
21#define ORES_QT_IMAGE_CACHE_HPP
22
23#include <string>
24#include <unordered_map>
25#include <unordered_set>
26#include <QObject>
27#include <QIcon>
28#include <QFutureWatcher>
29#include "ores.qt/ClientManager.hpp"
30#include "ores.logging/make_logger.hpp"
31#include "ores.assets.api/domain/image.hpp"
32#include "ores.assets.api/messaging/assets_protocol.hpp"
33
34namespace ores::qt {
35
53class ImageCache final : public QObject {
54 Q_OBJECT
55
56private:
57 inline static std::string_view logger_name = "ores.qt.image_cache";
58
59 [[nodiscard]] static auto& lg() {
60 using namespace ores::logging;
61 static auto instance = make_logger(logger_name);
62 return instance;
63 }
64
65public:
66 explicit ImageCache(ClientManager* clientManager, QObject* parent = nullptr);
67 ~ImageCache() override = default;
68
76 void loadAll();
77
84 void reload();
85
96 QIcon getIcon(const std::string& image_id);
97
104 bool hasIcon(const std::string& image_id) const;
105
109 std::size_t cachedIconCount() const { return image_icons_.size(); }
110
114 bool isLoading() const { return is_loading_images_; }
115
124 void clear();
125
132 void loadImageList();
133
139 const std::vector<assets::messaging::image_info>& availableImages() const {
140 return available_images_;
141 }
142
146 bool hasImageList() const { return !available_images_.empty(); }
147
155
163 void setCurrencyImage(const std::string& iso_code, const std::string& image_id,
164 const std::string& assigned_by);
165
173 void setCountryImage(const std::string& alpha2_code, const std::string& image_id,
174 const std::string& assigned_by);
175
181 std::string getNoFlagImageId() const;
182
188 QIcon getNoFlagIcon() const;
189
196 QIcon getCurrencyFlagIcon(const std::string& iso_code);
197
204 QIcon getCountryFlagIcon(const std::string& alpha2_code);
205
212 QIcon getBusinessCentreFlagIcon(const std::string& bc_code);
213
214signals:
219
223 void allLoaded();
224
228 void loadError(const QString& error_message);
229
234
240 void imageLoaded(const QString& image_id);
241
246
250 void currencyImageSet(const QString& iso_code, bool success, const QString& message);
251
255 void countryImageSet(const QString& alpha2_code, bool success, const QString& message);
256
257private slots:
258 void onCurrencyImageIdsLoaded();
259 void onCountryImageIdsLoaded();
260 void onBusinessCentreMappingLoaded();
261 void onImagesLoaded();
262 void onImageListLoaded();
263 void onSingleImageLoaded();
264 void onCurrencyImageSet();
265 void onCountryImageSet();
266 void onAllAvailableImagesLoaded();
267 void onIncrementalChangesLoaded();
268
269private:
276 static QIcon svgToIcon(const std::string& svg_data);
277
283 void loadImageById(const std::string& image_id);
284
288 void loadCurrencyImageIds();
289
293 void loadCountryImageIds();
294
298 void loadImagesByIds(const std::vector<std::string>& image_ids);
299
303 void loadBusinessCentreMapping();
304
310 void loadIncrementalChanges();
311
312 struct ImageIdsResult {
313 bool success;
314 std::vector<std::string> image_ids;
315 // Code -> image_id mappings populated during fetch
316 std::unordered_map<std::string, std::string> code_to_image_id;
317 };
318
319 struct BusinessCentreMappingResult {
320 bool success;
321 std::unordered_map<std::string, std::string> bc_to_country;
322 };
323
324 struct ImagesResult {
325 bool success;
326 std::vector<assets::domain::image> images;
327 int failed_batches{0};
328 };
329
337 static ImagesResult fetchImagesInBatches(
338 ClientManager* clientManager,
339 const std::vector<std::string>& image_ids);
340
341 struct ImageListResult {
342 bool success;
343 std::vector<assets::messaging::image_info> images;
344 };
345
346 struct SingleImageResult {
347 bool success;
348 std::string image_id;
350 };
351
352 struct SetCurrencyImageResult {
353 bool success;
354 std::string iso_code;
355 std::string message;
356 };
357
358 struct SetCountryImageResult {
359 bool success;
360 std::string alpha2_code;
361 std::string message;
362 };
363
364 ClientManager* clientManager_;
365
366 // image_id -> cached SVG data
367 std::unordered_map<std::string, std::string> image_svg_cache_;
368
369 // image_id -> QIcon (rendered from SVG)
370 std::unordered_map<std::string, QIcon> image_icons_;
371
372 // Loading state
373 bool is_loading_images_{false};
374 bool is_loading_all_available_{false};
375 bool load_all_in_progress_{false};
376
377 // Image IDs collected during loadAll() for preloading
378 std::vector<std::string> pending_image_ids_;
379
380 QFutureWatcher<ImageIdsResult>* currency_ids_watcher_;
381 QFutureWatcher<ImageIdsResult>* country_ids_watcher_;
382 QFutureWatcher<ImageIdsResult>* incremental_changes_watcher_;
383 QFutureWatcher<ImagesResult>* images_watcher_;
384 QFutureWatcher<ImageListResult>* image_list_watcher_;
385 QFutureWatcher<SingleImageResult>* single_image_watcher_;
386 QFutureWatcher<SetCurrencyImageResult>* set_currency_image_watcher_;
387 QFutureWatcher<SetCountryImageResult>* set_country_image_watcher_;
388 QFutureWatcher<ImagesResult>* all_available_watcher_;
389
390 // List of all available images (metadata only)
391 std::vector<assets::messaging::image_info> available_images_;
392
393 // Track image IDs currently being loaded to prevent duplicate requests
394 std::unordered_set<std::string> pending_image_requests_;
395
396 // Timestamp of last successful load (for incremental loading)
397 std::optional<std::chrono::system_clock::time_point> last_load_time_;
398
399 // Code -> image_id mappings for flag icon lookups
400 std::unordered_map<std::string, std::string> currency_iso_to_image_id_;
401 std::unordered_map<std::string, std::string> country_alpha2_to_image_id_;
402 std::unordered_map<std::string, std::string> bc_code_to_country_alpha2_;
403
404 QFutureWatcher<BusinessCentreMappingResult>* bc_mapping_watcher_;
405};
406
407}
408
409#endif
Implements logging infrastructure for ORE Studio.
Definition boost_severity.hpp:28
Qt-based graphical user interface for ORE Studio.
Definition AboutDialog.hpp:29
Represents a dynamically loaded image (typically SVG).
Definition image.hpp:34
Manages the lifecycle of the NATS client and login state.
Definition ClientManager.hpp:109
Cache for dynamically loaded images (flags, icons) from the server.
Definition ImageCache.hpp:53
QIcon getNoFlagIcon() const
Get the icon for the "no-flag" placeholder.
Definition ImageCache.cpp:847
void loadImageList()
Load list of all available images from the server.
Definition ImageCache.cpp:713
void imageLoaded(const QString &image_id)
Emitted when a single image has been loaded.
std::size_t cachedIconCount() const
Get the number of cached images.
Definition ImageCache.hpp:109
void countryImageSet(const QString &alpha2_code, bool success, const QString &message)
Emitted when country image assignment is complete.
void allLoaded()
Emitted when all data has been loaded (after loadAll()).
QIcon getCurrencyFlagIcon(const std::string &iso_code)
Get flag icon for a currency by its ISO code.
Definition ImageCache.cpp:367
bool hasIcon(const std::string &image_id) const
Check if an image is cached.
Definition ImageCache.cpp:582
void setCountryImage(const std::string &alpha2_code, const std::string &image_id, const std::string &assigned_by)
Set or remove a country's image association.
Definition ImageCache.cpp:959
const std::vector< assets::messaging::image_info > & availableImages() const
Get the list of available images.
Definition ImageCache.hpp:139
bool isLoading() const
Check if images are currently being loaded.
Definition ImageCache.hpp:114
bool hasImageList() const
Check if image list has been loaded.
Definition ImageCache.hpp:146
QIcon getCountryFlagIcon(const std::string &alpha2_code)
Get flag icon for a country by its alpha-2 code.
Definition ImageCache.cpp:375
std::string getNoFlagImageId() const
Get the image ID for the "no-flag" placeholder.
Definition ImageCache.cpp:837
QIcon getBusinessCentreFlagIcon(const std::string &bc_code)
Get flag icon for a business centre by its code.
Definition ImageCache.cpp:383
void loadAllAvailableImages()
Load all available images from the image list.
Definition ImageCache.cpp:761
void setCurrencyImage(const std::string &iso_code, const std::string &image_id, const std::string &assigned_by)
Set or remove a currency's image association.
Definition ImageCache.cpp:871
void imageListLoaded()
Emitted when image list has been loaded.
void loadAll()
Preload images for current currencies and countries.
Definition ImageCache.cpp:79
void clear()
Clear all caches and reset load state.
Definition ImageCache.cpp:133
void loadError(const QString &error_message)
Emitted when an error occurs during loading.
void imagesLoaded()
Emitted when images have been loaded.
void currencyImageSet(const QString &iso_code, bool success, const QString &message)
Emitted when currency image assignment is complete.
QIcon getIcon(const std::string &image_id)
Get icon for an image by its UUID.
Definition ImageCache.cpp:562
void allAvailableImagesLoaded()
Emitted when all available images have been loaded.
void reload()
Clear all caches and reload images.
Definition ImageCache.cpp:94