mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-16 05:42:32 +00:00
Avoid accessing Preferences from background thread when loading preset palettes
This commit is contained in:
parent
79795b97a9
commit
f430b9ce77
@ -542,6 +542,7 @@ add_library(app-lib
|
|||||||
file/file_data.cpp
|
file/file_data.cpp
|
||||||
file/file_format.cpp
|
file/file_format.cpp
|
||||||
file/file_formats_manager.cpp
|
file/file_formats_manager.cpp
|
||||||
|
file/file_op_config.cpp
|
||||||
file/palette_file.cpp
|
file/palette_file.cpp
|
||||||
file/split_filename.cpp
|
file/split_filename.cpp
|
||||||
file_system.cpp
|
file_system.cpp
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include "render/render.h"
|
#include "render/render.h"
|
||||||
#include "ui/alert.h"
|
#include "ui/alert.h"
|
||||||
#include "ui/listitem.h"
|
#include "ui/listitem.h"
|
||||||
|
#include "ui/system.h"
|
||||||
|
|
||||||
#include "ask_for_color_profile.xml.h"
|
#include "ask_for_color_profile.xml.h"
|
||||||
#include "open_sequence.xml.h"
|
#include "open_sequence.xml.h"
|
||||||
@ -177,10 +178,13 @@ FileOpROI::FileOpROI(const Doc* doc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
FileOp* FileOp::createLoadDocumentOperation(Context* context, const std::string& filename, int flags)
|
FileOp* FileOp::createLoadDocumentOperation(Context* context,
|
||||||
|
const std::string& filename,
|
||||||
|
const int flags,
|
||||||
|
const FileOpConfig* config)
|
||||||
{
|
{
|
||||||
std::unique_ptr<FileOp> fop(
|
std::unique_ptr<FileOp> fop(
|
||||||
new FileOp(FileOpLoad, context));
|
new FileOp(FileOpLoad, context, config));
|
||||||
if (!fop)
|
if (!fop)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
@ -329,7 +333,7 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context,
|
|||||||
const bool ignoreEmptyFrames)
|
const bool ignoreEmptyFrames)
|
||||||
{
|
{
|
||||||
std::unique_ptr<FileOp> fop(
|
std::unique_ptr<FileOp> fop(
|
||||||
new FileOp(FileOpSave, const_cast<Context*>(context)));
|
new FileOp(FileOpSave, const_cast<Context*>(context), nullptr));
|
||||||
|
|
||||||
// Document to save
|
// Document to save
|
||||||
fop->m_document = const_cast<Doc*>(roi.document());
|
fop->m_document = const_cast<Doc*>(roi.document());
|
||||||
@ -711,7 +715,7 @@ void FileOp::operate(IFileOpProgress* progress)
|
|||||||
try {
|
try {
|
||||||
load_aseprite_data_file(m_dataFilename,
|
load_aseprite_data_file(m_dataFilename,
|
||||||
m_document,
|
m_document,
|
||||||
m_defaultSliceColor);
|
m_config.defaultSliceColor);
|
||||||
}
|
}
|
||||||
catch (const std::exception& ex) {
|
catch (const std::exception& ex) {
|
||||||
setError("Error loading data file: %s\n", ex.what());
|
setError("Error loading data file: %s\n", ex.what());
|
||||||
@ -739,7 +743,7 @@ void FileOp::operate(IFileOpProgress* progress)
|
|||||||
|
|
||||||
// For each frame in the sprite.
|
// For each frame in the sprite.
|
||||||
render::Render render;
|
render::Render render;
|
||||||
render.setNewBlend(m_newBlend);
|
render.setNewBlend(m_config.newBlend);
|
||||||
|
|
||||||
frame_t outputFrame = 0;
|
frame_t outputFrame = 0;
|
||||||
for (frame_t frame : m_roi.selectedFrames()) {
|
for (frame_t frame : m_roi.selectedFrames()) {
|
||||||
@ -903,7 +907,7 @@ void FileOp::postLoad()
|
|||||||
base::SharedPtr<Palette> palette(
|
base::SharedPtr<Palette> palette(
|
||||||
render::create_palette_from_sprite(
|
render::create_palette_from_sprite(
|
||||||
sprite, frame_t(0), sprite->lastFrame(), true,
|
sprite, frame_t(0), sprite->lastFrame(), true,
|
||||||
nullptr, nullptr, Preferences::instance().experimental.newBlend()));
|
nullptr, nullptr, m_config.newBlend));
|
||||||
|
|
||||||
sprite->resetPalettes();
|
sprite->resetPalettes();
|
||||||
sprite->setPalette(palette.get(), false);
|
sprite->setPalette(palette.get(), false);
|
||||||
@ -915,10 +919,10 @@ void FileOp::postLoad()
|
|||||||
app::gen::ColorProfileBehavior behavior =
|
app::gen::ColorProfileBehavior behavior =
|
||||||
app::gen::ColorProfileBehavior::DISABLE;
|
app::gen::ColorProfileBehavior::DISABLE;
|
||||||
|
|
||||||
if (m_preserveColorProfile) {
|
if (m_config.preserveColorProfile) {
|
||||||
// Embedded color profile
|
// Embedded color profile
|
||||||
if (this->hasEmbeddedColorProfile()) {
|
if (this->hasEmbeddedColorProfile()) {
|
||||||
behavior = Preferences::instance().color.filesWithProfile();
|
behavior = m_config.filesWithProfile;
|
||||||
if (behavior == app::gen::ColorProfileBehavior::ASK) {
|
if (behavior == app::gen::ColorProfileBehavior::ASK) {
|
||||||
#ifdef ENABLE_UI
|
#ifdef ENABLE_UI
|
||||||
if (m_context && m_context->isUIAvailable()) {
|
if (m_context && m_context->isUIAvailable()) {
|
||||||
@ -944,7 +948,7 @@ void FileOp::postLoad()
|
|||||||
}
|
}
|
||||||
// Missing color space
|
// Missing color space
|
||||||
else {
|
else {
|
||||||
behavior = Preferences::instance().color.missingProfile();
|
behavior = m_config.missingProfile;
|
||||||
if (behavior == app::gen::ColorProfileBehavior::ASK) {
|
if (behavior == app::gen::ColorProfileBehavior::ASK) {
|
||||||
#ifdef ENABLE_UI
|
#ifdef ENABLE_UI
|
||||||
if (m_context && m_context->isUIAvailable()) {
|
if (m_context && m_context->isUIAvailable()) {
|
||||||
@ -982,7 +986,7 @@ void FileOp::postLoad()
|
|||||||
|
|
||||||
case app::gen::ColorProfileBehavior::CONVERT: {
|
case app::gen::ColorProfileBehavior::CONVERT: {
|
||||||
// Convert to the working color profile
|
// Convert to the working color profile
|
||||||
auto gfxCS = get_working_rgb_space_from_preferences();
|
auto gfxCS = m_config.workingCS;
|
||||||
if (!gfxCS->nearlyEqual(*spriteCS))
|
if (!gfxCS->nearlyEqual(*spriteCS))
|
||||||
cmd::convert_color_profile(sprite, gfxCS);
|
cmd::convert_color_profile(sprite, gfxCS);
|
||||||
break;
|
break;
|
||||||
@ -990,7 +994,7 @@ void FileOp::postLoad()
|
|||||||
|
|
||||||
case app::gen::ColorProfileBehavior::ASSIGN: {
|
case app::gen::ColorProfileBehavior::ASSIGN: {
|
||||||
// Convert to the working color profile
|
// Convert to the working color profile
|
||||||
auto gfxCS = get_working_rgb_space_from_preferences();
|
auto gfxCS = m_config.workingCS;
|
||||||
sprite->setColorSpace(gfxCS);
|
sprite->setColorSpace(gfxCS);
|
||||||
m_document->notifyColorSpaceChanged();
|
m_document->notifyColorSpaceChanged();
|
||||||
break;
|
break;
|
||||||
@ -1178,7 +1182,9 @@ bool FileOp::isStop() const
|
|||||||
return stop;
|
return stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileOp::FileOp(FileOpType type, Context* context)
|
FileOp::FileOp(FileOpType type,
|
||||||
|
Context* context,
|
||||||
|
const FileOpConfig* config)
|
||||||
: m_type(type)
|
: m_type(type)
|
||||||
, m_format(nullptr)
|
, m_format(nullptr)
|
||||||
, m_context(context)
|
, m_context(context)
|
||||||
@ -1190,11 +1196,17 @@ FileOp::FileOp(FileOpType type, Context* context)
|
|||||||
, m_oneframe(false)
|
, m_oneframe(false)
|
||||||
, m_createPaletteFromRgba(false)
|
, m_createPaletteFromRgba(false)
|
||||||
, m_ignoreEmpty(false)
|
, m_ignoreEmpty(false)
|
||||||
, m_preserveColorProfile(Preferences::instance().color.manage())
|
|
||||||
, m_embeddedColorProfile(false)
|
, m_embeddedColorProfile(false)
|
||||||
, m_newBlend(Preferences::instance().experimental.newBlend())
|
|
||||||
, m_defaultSliceColor(Preferences::instance().slices.defaultColor())
|
|
||||||
{
|
{
|
||||||
|
if (config)
|
||||||
|
m_config = *config;
|
||||||
|
else if (ui::is_ui_thread())
|
||||||
|
m_config.fillFromPreferences();
|
||||||
|
else {
|
||||||
|
LOG(VERBOSE, "FILE: Using a file operation with default configuration\n");
|
||||||
|
ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
m_seq.palette = nullptr;
|
m_seq.palette = nullptr;
|
||||||
m_seq.image.reset();
|
m_seq.image.reset();
|
||||||
m_seq.progress_offset = 0.0f;
|
m_seq.progress_offset = 0.0f;
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "app/color.h"
|
#include "app/color.h"
|
||||||
|
#include "app/file/file_op_config.h"
|
||||||
|
#include "app/pref/preferences.h"
|
||||||
#include "base/mutex.h"
|
#include "base/mutex.h"
|
||||||
#include "base/paths.h"
|
#include "base/paths.h"
|
||||||
#include "base/shared_ptr.h"
|
#include "base/shared_ptr.h"
|
||||||
@ -102,7 +104,8 @@ namespace app {
|
|||||||
public:
|
public:
|
||||||
static FileOp* createLoadDocumentOperation(Context* context,
|
static FileOp* createLoadDocumentOperation(Context* context,
|
||||||
const std::string& filename,
|
const std::string& filename,
|
||||||
int flags);
|
const int flags,
|
||||||
|
const FileOpConfig* config = nullptr);
|
||||||
|
|
||||||
static FileOp* createSaveDocumentOperation(const Context* context,
|
static FileOp* createSaveDocumentOperation(const Context* context,
|
||||||
const FileOpROI& roi,
|
const FileOpROI& roi,
|
||||||
@ -114,7 +117,7 @@ namespace app {
|
|||||||
|
|
||||||
bool isSequence() const { return !m_seq.filename_list.empty(); }
|
bool isSequence() const { return !m_seq.filename_list.empty(); }
|
||||||
bool isOneFrame() const { return m_oneframe; }
|
bool isOneFrame() const { return m_oneframe; }
|
||||||
bool preserveColorProfile() const { return m_preserveColorProfile; }
|
bool preserveColorProfile() const { return m_config.preserveColorProfile; }
|
||||||
|
|
||||||
const std::string& filename() const { return m_filename; }
|
const std::string& filename() const { return m_filename; }
|
||||||
const base::paths& filenames() const { return m_seq.filename_list; }
|
const base::paths& filenames() const { return m_seq.filename_list; }
|
||||||
@ -175,11 +178,13 @@ namespace app {
|
|||||||
void setEmbeddedColorProfile() { m_embeddedColorProfile = true; }
|
void setEmbeddedColorProfile() { m_embeddedColorProfile = true; }
|
||||||
bool hasEmbeddedColorProfile() const { return m_embeddedColorProfile; }
|
bool hasEmbeddedColorProfile() const { return m_embeddedColorProfile; }
|
||||||
|
|
||||||
bool newBlend() const { return m_newBlend; }
|
bool newBlend() const { return m_config.newBlend; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FileOp(); // Undefined
|
FileOp(); // Undefined
|
||||||
FileOp(FileOpType type, Context* context);
|
FileOp(FileOpType type,
|
||||||
|
Context* context,
|
||||||
|
const FileOpConfig* config);
|
||||||
|
|
||||||
FileOpType m_type; // Operation type: 0=load, 1=save.
|
FileOpType m_type; // Operation type: 0=load, 1=save.
|
||||||
FileFormat* m_format;
|
FileFormat* m_format;
|
||||||
@ -204,18 +209,10 @@ namespace app {
|
|||||||
bool m_createPaletteFromRgba;
|
bool m_createPaletteFromRgba;
|
||||||
bool m_ignoreEmpty;
|
bool m_ignoreEmpty;
|
||||||
|
|
||||||
// Return if we've to save/embed the color space of the document
|
|
||||||
// in the file.
|
|
||||||
bool m_preserveColorProfile;
|
|
||||||
|
|
||||||
// True if the file contained a color profile when it was loaded.
|
// True if the file contained a color profile when it was loaded.
|
||||||
bool m_embeddedColorProfile;
|
bool m_embeddedColorProfile;
|
||||||
|
|
||||||
// True if we should render each frame to save it with the new
|
FileOpConfig m_config;
|
||||||
// blend mode.
|
|
||||||
bool m_newBlend;
|
|
||||||
|
|
||||||
app::Color m_defaultSliceColor;
|
|
||||||
|
|
||||||
base::SharedPtr<FormatOptions> m_formatOptions;
|
base::SharedPtr<FormatOptions> m_formatOptions;
|
||||||
|
|
||||||
|
27
src/app/file/file_op_config.cpp
Normal file
27
src/app/file/file_op_config.cpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2019 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This program is distributed under the terms of
|
||||||
|
// the End-User License Agreement for Aseprite.
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "app/file/file_op_config.h"
|
||||||
|
|
||||||
|
#include "app/color_spaces.h"
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
void FileOpConfig::fillFromPreferences()
|
||||||
|
{
|
||||||
|
preserveColorProfile = Preferences::instance().color.manage();
|
||||||
|
filesWithProfile = Preferences::instance().color.filesWithProfile();
|
||||||
|
missingProfile = Preferences::instance().color.missingProfile();
|
||||||
|
newBlend = Preferences::instance().experimental.newBlend();
|
||||||
|
defaultSliceColor = Preferences::instance().slices.defaultColor();
|
||||||
|
workingCS = get_working_rgb_space_from_preferences();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace app
|
40
src/app/file/file_op_config.h
Normal file
40
src/app/file/file_op_config.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2019 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This program is distributed under the terms of
|
||||||
|
// the End-User License Agreement for Aseprite.
|
||||||
|
|
||||||
|
#ifndef APP_FILE_FILE_OP_CONFIG_H_INCLUDED
|
||||||
|
#define APP_FILE_FILE_OP_CONFIG_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "app/color.h"
|
||||||
|
#include "app/pref/preferences.h"
|
||||||
|
#include "gfx/color_space.h"
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
// Options that came from Preferences but can be used in the non-UI thread.
|
||||||
|
struct FileOpConfig {
|
||||||
|
// True if we have to save/embed the color space of the document
|
||||||
|
// in the file.
|
||||||
|
bool preserveColorProfile = true;
|
||||||
|
|
||||||
|
// Configuration of what to do when we load a file with a color
|
||||||
|
// profile or without a color profile.
|
||||||
|
app::gen::ColorProfileBehavior filesWithProfile = app::gen::ColorProfileBehavior::EMBEDDED;
|
||||||
|
app::gen::ColorProfileBehavior missingProfile = app::gen::ColorProfileBehavior::ASSIGN;
|
||||||
|
gfx::ColorSpacePtr workingCS = gfx::ColorSpace::MakeSRGB();
|
||||||
|
|
||||||
|
// True if we should render each frame to save it with the new
|
||||||
|
// blend mode.h
|
||||||
|
bool newBlend = true;
|
||||||
|
|
||||||
|
app::Color defaultSliceColor = app::Color::fromRgb(0, 0, 255);
|
||||||
|
|
||||||
|
void fillFromPreferences();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace app
|
||||||
|
|
||||||
|
#endif
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2018 Igara Studio S.A.
|
// Copyright (C) 2018-2019 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -52,7 +52,8 @@ base::paths get_writable_palette_extensions()
|
|||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
Palette* load_palette(const char* filename)
|
Palette* load_palette(const char* filename,
|
||||||
|
const FileOpConfig* config)
|
||||||
{
|
{
|
||||||
dio::FileFormat dioFormat = dio::detect_format(filename);
|
dio::FileFormat dioFormat = dio::detect_format(filename);
|
||||||
Palette* pal = nullptr;
|
Palette* pal = nullptr;
|
||||||
@ -89,7 +90,8 @@ Palette* load_palette(const char* filename)
|
|||||||
nullptr, filename,
|
nullptr, filename,
|
||||||
FILE_LOAD_CREATE_PALETTE |
|
FILE_LOAD_CREATE_PALETTE |
|
||||||
FILE_LOAD_SEQUENCE_NONE |
|
FILE_LOAD_SEQUENCE_NONE |
|
||||||
FILE_LOAD_ONE_FRAME));
|
FILE_LOAD_ONE_FRAME,
|
||||||
|
config));
|
||||||
|
|
||||||
if (fop && !fop->hasError()) {
|
if (fop && !fop->hasError()) {
|
||||||
fop->operate(nullptr);
|
fop->operate(nullptr);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2019 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -15,11 +16,13 @@ namespace doc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
struct FileOpConfig;
|
||||||
|
|
||||||
base::paths get_readable_palette_extensions();
|
base::paths get_readable_palette_extensions();
|
||||||
base::paths get_writable_palette_extensions();
|
base::paths get_writable_palette_extensions();
|
||||||
|
|
||||||
doc::Palette* load_palette(const char *filename);
|
doc::Palette* load_palette(const char *filename,
|
||||||
|
const FileOpConfig* config = nullptr);
|
||||||
bool save_palette(const char *filename, const doc::Palette* pal,
|
bool save_palette(const char *filename, const doc::Palette* pal,
|
||||||
int columns);
|
int columns);
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2019 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2017 David Capello
|
// Copyright (C) 2001-2017 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -20,9 +21,17 @@
|
|||||||
#include "base/fs.h"
|
#include "base/fs.h"
|
||||||
#include "base/scoped_value.h"
|
#include "base/scoped_value.h"
|
||||||
#include "doc/palette.h"
|
#include "doc/palette.h"
|
||||||
|
#include "ui/system.h"
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
|
PalettesLoaderDelegate::PalettesLoaderDelegate()
|
||||||
|
{
|
||||||
|
// Necessary to load preferences in the UI-thread which will be used
|
||||||
|
// in a FileOp executed in a background thread.
|
||||||
|
m_config.fillFromPreferences();
|
||||||
|
}
|
||||||
|
|
||||||
void PalettesLoaderDelegate::getResourcesPaths(std::map<std::string, std::string>& idAndPath) const
|
void PalettesLoaderDelegate::getResourcesPaths(std::map<std::string, std::string>& idAndPath) const
|
||||||
{
|
{
|
||||||
// Include extension palettes
|
// Include extension palettes
|
||||||
@ -55,7 +64,7 @@ void PalettesLoaderDelegate::getResourcesPaths(std::map<std::string, std::string
|
|||||||
Resource* PalettesLoaderDelegate::loadResource(const std::string& id,
|
Resource* PalettesLoaderDelegate::loadResource(const std::string& id,
|
||||||
const std::string& path)
|
const std::string& path)
|
||||||
{
|
{
|
||||||
doc::Palette* palette = load_palette(path.c_str());
|
doc::Palette* palette = load_palette(path.c_str(), &m_config);
|
||||||
if (palette)
|
if (palette)
|
||||||
return new PaletteResource(id, path, palette);
|
return new PaletteResource(id, path, palette);
|
||||||
else
|
else
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2019 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2017 David Capello
|
// Copyright (C) 2001-2017 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -8,16 +9,22 @@
|
|||||||
#define APP_RES_PALETTES_LOADER_DELEGATE_H_INCLUDED
|
#define APP_RES_PALETTES_LOADER_DELEGATE_H_INCLUDED
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "app/file/file_op_config.h"
|
||||||
#include "app/res/resources_loader_delegate.h"
|
#include "app/res/resources_loader_delegate.h"
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
class PalettesLoaderDelegate : public ResourcesLoaderDelegate {
|
class PalettesLoaderDelegate : public ResourcesLoaderDelegate {
|
||||||
public:
|
public:
|
||||||
|
PalettesLoaderDelegate();
|
||||||
|
|
||||||
// ResourcesLoaderDelegate impl
|
// ResourcesLoaderDelegate impl
|
||||||
virtual void getResourcesPaths(std::map<std::string, std::string>& idAndPath) const override;
|
virtual void getResourcesPaths(std::map<std::string, std::string>& idAndPath) const override;
|
||||||
virtual Resource* loadResource(const std::string& id,
|
virtual Resource* loadResource(const std::string& id,
|
||||||
const std::string& path) override;
|
const std::string& path) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FileOpConfig m_config;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
Loading…
x
Reference in New Issue
Block a user