mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-02-19 06:26:55 +00:00
Use most-filled document from OneOf list.
This commit is contained in:
parent
e25ecce887
commit
82a3cd9bdb
@ -1563,7 +1563,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_passport_identity_internal" = "Internal passport";
|
||||
"lng_passport_identity_internal_upload" = "Upload a scan of your internal passport";
|
||||
"lng_passport_identity_about" = "The document must contain your photograph, first and last name, date of birth, document number, country of issue, and expiry date.";
|
||||
"lng_passport_identity_selfie" = "Take a selfie with your document";
|
||||
"lng_passport_address_title" = "Residential address";
|
||||
"lng_passport_address_description" = "Upload a proof of your address";
|
||||
"lng_passport_address_bill" = "Utility bill";
|
||||
@ -1584,6 +1583,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_passport_phone_description" = "Enter your phone number";
|
||||
"lng_passport_email_title" = "Email";
|
||||
"lng_passport_email_description" = "Enter your email address";
|
||||
"lng_passport_identity_selfie" = "Take a selfie with your document";
|
||||
"lng_passport_translation_needed" = "Upload a translation of your document";
|
||||
"lng_passport_accept_allow" = "You accept the {policy} and allow their {bot} to send you messages.";
|
||||
"lng_passport_allow" = "You allow {bot} to send you messages.";
|
||||
"lng_passport_policy" = "{bot} privacy policy";
|
||||
@ -1596,27 +1597,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_passport_upload_scans" = "Upload scans";
|
||||
"lng_passport_upload_more" = "Upload additional scans";
|
||||
"lng_passport_selfie_title" = "Selfie";
|
||||
"lng_passport_selfie_name" = "Photo";
|
||||
"lng_passport_selfie_description" = "Upload a photo of yourself holding your document. Make sure the ID and your face are clearly visible.";
|
||||
"lng_passport_upload_selfie" = "Upload selfie";
|
||||
"lng_passport_reupload_selfie" = "Reupload selfie";
|
||||
"lng_passport_front_side_title" = "Front side";
|
||||
"lng_passport_front_side_name" = "Scan";
|
||||
"lng_passport_front_side_description" = "Upload the front side of your document.";
|
||||
"lng_passport_upload_front_side" = "Upload a scan of the front side";
|
||||
"lng_passport_reupload_front_side" = "Reupload a scan of the front side";
|
||||
"lng_passport_reverse_side_title" = "Reverse side";
|
||||
"lng_passport_reverse_side_name" = "Scan";
|
||||
"lng_passport_reverse_side_description" = "Upload the reverse side of your document.";
|
||||
"lng_passport_upload_reverse_side" = "Upload a scan of the reverse side";
|
||||
"lng_passport_reupload_reverse_side" = "Reupload a scan of the reverse side";
|
||||
"lng_passport_main_page_title" = "Main page";
|
||||
"lng_passport_main_page_name" = "Scan";
|
||||
"lng_passport_main_page_description" = "Upload the main page of your document.";
|
||||
"lng_passport_upload_main_page" = "Upload a scan of the main page";
|
||||
"lng_passport_reupload_main_page" = "Reupload a scan of the main page";
|
||||
"lng_passport_personal_details" = "Personal details";
|
||||
"lng_passport_personal_details_enter" = "Enter your personal details";
|
||||
"lng_passport_personal_details_enter" = "Fill in your personal details";
|
||||
"lng_passport_document_details" = "Document details";
|
||||
"lng_passport_choose_image" = "Choose scan image";
|
||||
"lng_passport_delete_scan_undo" = "Undo";
|
||||
@ -1636,7 +1633,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_passport_native_name_title" = "Name in document language";
|
||||
"lng_passport_native_name_about" = "Your name in the language of the country ({country}) that issued the document.";
|
||||
"lng_passport_address" = "Address";
|
||||
"lng_passport_address_enter" = "Enter your address";
|
||||
"lng_passport_address_enter" = "Please provide your address";
|
||||
"lng_passport_street" = "Street";
|
||||
"lng_passport_city" = "City";
|
||||
"lng_passport_state" = "State";
|
||||
|
@ -386,23 +386,28 @@ void Value::fillDataFrom(Value &&other) {
|
||||
}
|
||||
|
||||
bool Value::scansAreFilled() const {
|
||||
if (requiresScan(FileType::Translation) && _translations.empty()) {
|
||||
return false;
|
||||
} else if (requiresScan(FileType::Scan) && _scans.empty()) {
|
||||
return false;
|
||||
}
|
||||
const auto types = {
|
||||
FileType::FrontSide,
|
||||
FileType::ReverseSide,
|
||||
FileType::Selfie
|
||||
return (whatNotFilled() == 0);
|
||||
}
|
||||
|
||||
int Value::whatNotFilled() const {
|
||||
const auto noRequiredSpecialScan = [&](FileType type) {
|
||||
return requiresSpecialScan(type)
|
||||
&& (specialScans.find(type) == end(specialScans));
|
||||
};
|
||||
for (const auto type : types) {
|
||||
if (requiresSpecialScan(type)
|
||||
&& (specialScans.find(type) == end(specialScans))) {
|
||||
return false;
|
||||
}
|
||||
if (requiresScan(FileType::Scan) && _scans.empty()) {
|
||||
return kNothingFilled;
|
||||
} else if (noRequiredSpecialScan(FileType::FrontSide)) {
|
||||
return kNothingFilled;
|
||||
}
|
||||
return true;
|
||||
auto result = 0;
|
||||
if (requiresScan(FileType::Translation) && _translations.empty()) {
|
||||
result |= kNoTranslationFilled;
|
||||
}
|
||||
if (noRequiredSpecialScan(FileType::ReverseSide)
|
||||
|| noRequiredSpecialScan(FileType::Selfie)) {
|
||||
result |= kNoSelfieFilled;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Value::saveInEdit() {
|
||||
|
@ -186,6 +186,11 @@ struct Value {
|
||||
bool uploadingScan() const;
|
||||
bool saving() const;
|
||||
|
||||
static constexpr auto kNothingFilled = 0x100;
|
||||
static constexpr auto kNoTranslationFilled = 0x10;
|
||||
static constexpr auto kNoSelfieFilled = 0x001;
|
||||
int whatNotFilled() const;
|
||||
|
||||
std::vector<File> &files(FileType type);
|
||||
const std::vector<File> &files(FileType type) const;
|
||||
QString &fileMissingError(FileType type);
|
||||
|
@ -479,18 +479,28 @@ ScopeRow ComputeScopeRow(const Scope &scope) {
|
||||
row.title = titleFallback;
|
||||
}
|
||||
|
||||
// #TODO passport half-full value
|
||||
//if (row.error.isEmpty()
|
||||
// && row.ready.isEmpty()
|
||||
// && scope.type == Scope::Type::Identity
|
||||
// && scope.selfieRequired) {
|
||||
// auto noSelfieScope = scope;
|
||||
// noSelfieScope.selfieRequired = false;
|
||||
// if (!ComputeScopeRowReadyString(noSelfieScope).isEmpty()) {
|
||||
// // Only selfie is missing.
|
||||
// row.description = lang(lng_passport_identity_selfie);
|
||||
// }
|
||||
//}
|
||||
if (row.error.isEmpty()
|
||||
&& row.ready.isEmpty()
|
||||
&& !scope.documents.empty()) {
|
||||
if (document) {
|
||||
row.description = (scope.type == Scope::Type::Identity)
|
||||
? lang(lng_passport_personal_details_enter)
|
||||
: lang(lng_passport_address_enter);
|
||||
} else {
|
||||
const auto best = ranges::min(
|
||||
scope.documents,
|
||||
std::less<>(),
|
||||
[](not_null<const Value*> document) {
|
||||
return document->whatNotFilled();
|
||||
});
|
||||
const auto notFilled = best->whatNotFilled();
|
||||
if (notFilled & Value::kNoTranslationFilled) {
|
||||
row.description = lang(lng_passport_translation_needed);
|
||||
} else if (notFilled & Value::kNoSelfieFilled) {
|
||||
row.description = lang(lng_passport_identity_selfie);
|
||||
}
|
||||
}
|
||||
}
|
||||
return row;
|
||||
};
|
||||
switch (scope.type) {
|
||||
|
@ -64,11 +64,11 @@ ScanInfo CollectScanInfo(const EditFile &file) {
|
||||
}
|
||||
}();
|
||||
return {
|
||||
file.type,
|
||||
FileKey{ file.fields.id, file.fields.dcId },
|
||||
!file.fields.error.isEmpty() ? file.fields.error : status,
|
||||
file.fields.image,
|
||||
file.deleted,
|
||||
file.type,
|
||||
file.fields.error };
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ std::map<FileType, ScanInfo> PrepareSpecialFiles(const Value &value) {
|
||||
type,
|
||||
(i != end(value.specialScansInEdit)
|
||||
? CollectScanInfo(i->second)
|
||||
: ScanInfo())).first;
|
||||
: ScanInfo(type))).first;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -185,22 +185,20 @@ EditDocumentScheme GetDocumentScheme(
|
||||
result.detailsHeader = lang(lng_passport_personal_details);
|
||||
result.fieldsHeader = lang(lng_passport_document_details);
|
||||
if (scansType) {
|
||||
switch (*scansType) {
|
||||
case Value::Type::Passport:
|
||||
result.scansHeader = lang(lng_passport_identity_passport);
|
||||
break;
|
||||
case Value::Type::DriverLicense:
|
||||
result.scansHeader = lang(lng_passport_identity_license);
|
||||
break;
|
||||
case Value::Type::IdentityCard:
|
||||
result.scansHeader = lang(lng_passport_identity_card);
|
||||
break;
|
||||
case Value::Type::InternalPassport:
|
||||
result.scansHeader = lang(lng_passport_identity_internal);
|
||||
break;
|
||||
default:
|
||||
Unexpected("scansType in GetDocumentScheme:Identity.");
|
||||
}
|
||||
result.scansHeader = [&] {
|
||||
switch (*scansType) {
|
||||
case Value::Type::Passport:
|
||||
return lang(lng_passport_identity_passport);
|
||||
case Value::Type::DriverLicense:
|
||||
return lang(lng_passport_identity_license);
|
||||
case Value::Type::IdentityCard:
|
||||
return lang(lng_passport_identity_card);
|
||||
case Value::Type::InternalPassport:
|
||||
return lang(lng_passport_identity_internal);
|
||||
default:
|
||||
Unexpected("scansType in GetDocumentScheme:Identity.");
|
||||
}
|
||||
}();
|
||||
}
|
||||
result.rows = {
|
||||
{
|
||||
@ -501,6 +499,24 @@ bool SkipFieldCheck(not_null<const Value*> value, const QString &key) {
|
||||
return dontCheckNames.find(key) != end(dontCheckNames);
|
||||
}
|
||||
|
||||
ScanInfo::ScanInfo(FileType type) : type(type) {
|
||||
}
|
||||
|
||||
ScanInfo::ScanInfo(
|
||||
FileType type,
|
||||
const FileKey &key,
|
||||
const QString &status,
|
||||
const QImage &thumb,
|
||||
bool deleted,
|
||||
const QString &error)
|
||||
: type(type)
|
||||
, key(key)
|
||||
, status(status)
|
||||
, thumb(thumb)
|
||||
, deleted(deleted)
|
||||
, error(error) {
|
||||
}
|
||||
|
||||
BoxPointer::BoxPointer(QPointer<BoxContent> value)
|
||||
: _value(value) {
|
||||
}
|
||||
@ -912,41 +928,34 @@ void PanelController::ensurePanelCreated() {
|
||||
}
|
||||
}
|
||||
|
||||
int PanelController::findNonEmptyDocumentIndex(const Scope &scope) const {
|
||||
base::optional<int> PanelController::findBestDocumentIndex(
|
||||
const Scope &scope) const {
|
||||
Expects(!scope.documents.empty());
|
||||
|
||||
const auto &documents = scope.documents;
|
||||
const auto i = ranges::find_if(
|
||||
const auto i = ranges::min_element(
|
||||
documents,
|
||||
std::less<>(),
|
||||
[](not_null<const Value*> document) {
|
||||
return document->scansAreFilled();
|
||||
return document->whatNotFilled();
|
||||
});
|
||||
if (i != end(documents)) {
|
||||
return (i - begin(documents));
|
||||
}
|
||||
// If we have a document where only selfie is not filled - return it.
|
||||
// #TODO passport half-full value
|
||||
//const auto j = ranges::find_if(
|
||||
// documents,
|
||||
// [&](not_null<const Value*> document) {
|
||||
// return document->scansAreFilled(false);
|
||||
// });
|
||||
//if (j != end(documents)) {
|
||||
// return (j - begin(documents));
|
||||
//}
|
||||
return ((*i)->whatNotFilled() == Value::kNothingFilled)
|
||||
? base::none
|
||||
: base::make_optional(int(i - begin(documents)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void PanelController::editScope(int index) {
|
||||
Expects(_panel != nullptr);
|
||||
Expects(index >= 0 && index < _scopes.size());
|
||||
|
||||
const auto &scope = _scopes[index];
|
||||
if (scope.documents.empty()) {
|
||||
editScope(index, -1);
|
||||
editScope(index, base::none);
|
||||
} else {
|
||||
const auto documentIndex = findNonEmptyDocumentIndex(scope);
|
||||
if (documentIndex >= 0 || scope.documents.size() == 1) {
|
||||
editScope(index, (documentIndex >= 0) ? documentIndex : 0);
|
||||
const auto documentIndex = findBestDocumentIndex(scope);
|
||||
if (documentIndex || scope.documents.size() == 1) {
|
||||
editScope(index, documentIndex ? *documentIndex : 0);
|
||||
} else {
|
||||
requestScopeFilesType(index);
|
||||
}
|
||||
@ -1056,16 +1065,16 @@ void PanelController::readScanError(ReadScanError error) {
|
||||
|
||||
bool PanelController::editRequiresScanUpload(
|
||||
int index,
|
||||
int documentIndex) const {
|
||||
base::optional<int> documentIndex) const {
|
||||
Expects(index >= 0 && index < _scopes.size());
|
||||
Expects((documentIndex < 0)
|
||||
|| (documentIndex >= 0
|
||||
&& documentIndex < _scopes[index].documents.size()));
|
||||
Expects(!documentIndex
|
||||
|| (*documentIndex >= 0
|
||||
&& *documentIndex < _scopes[index].documents.size()));
|
||||
|
||||
if (documentIndex < 0) {
|
||||
if (!documentIndex) {
|
||||
return false;
|
||||
}
|
||||
const auto document = _scopes[index].documents[documentIndex];
|
||||
const auto document = _scopes[index].documents[*documentIndex];
|
||||
if (document->requiresSpecialScan(FileType::FrontSide)) {
|
||||
const auto &scans = document->specialScans;
|
||||
return (scans.find(FileType::FrontSide) == end(scans));
|
||||
@ -1073,26 +1082,30 @@ bool PanelController::editRequiresScanUpload(
|
||||
return document->files(FileType::Scan).empty();
|
||||
}
|
||||
|
||||
void PanelController::editScope(int index, int documentIndex) {
|
||||
void PanelController::editScope(
|
||||
int index,
|
||||
base::optional<int> documentIndex) {
|
||||
if (editRequiresScanUpload(index, documentIndex)) {
|
||||
editWithUpload(index, documentIndex);
|
||||
editWithUpload(index, *documentIndex);
|
||||
} else {
|
||||
startScopeEdit(index, documentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void PanelController::startScopeEdit(int index, int documentIndex) {
|
||||
void PanelController::startScopeEdit(
|
||||
int index,
|
||||
base::optional<int> documentIndex) {
|
||||
Expects(_panel != nullptr);
|
||||
Expects(index >= 0 && index < _scopes.size());
|
||||
Expects(_scopes[index].details != 0 || documentIndex >= 0);
|
||||
Expects((documentIndex < 0)
|
||||
|| (documentIndex >= 0
|
||||
&& documentIndex < _scopes[index].documents.size()));
|
||||
Expects(_scopes[index].details != 0 || documentIndex.has_value());
|
||||
Expects(!documentIndex.has_value()
|
||||
|| (*documentIndex >= 0
|
||||
&& *documentIndex < _scopes[index].documents.size()));
|
||||
|
||||
_editScope = &_scopes[index];
|
||||
_editValue = _editScope->details;
|
||||
_editDocument = (documentIndex >= 0)
|
||||
? _scopes[index].documents[documentIndex].get()
|
||||
_editDocument = documentIndex
|
||||
? _scopes[index].documents[*documentIndex].get()
|
||||
: nullptr;
|
||||
|
||||
if (_editValue) {
|
||||
|
@ -32,11 +32,20 @@ QString AdjustKeyName(not_null<const Value*> value, const QString &key);
|
||||
bool SkipFieldCheck(not_null<const Value*> value, const QString &key);
|
||||
|
||||
struct ScanInfo {
|
||||
explicit ScanInfo(FileType type);
|
||||
ScanInfo(
|
||||
FileType type,
|
||||
const FileKey &key,
|
||||
const QString &status,
|
||||
const QImage &thumb,
|
||||
bool deleted,
|
||||
const QString &error);
|
||||
|
||||
FileType type;
|
||||
FileKey key;
|
||||
QString status;
|
||||
QImage thumb;
|
||||
bool deleted = false;
|
||||
FileType type = FileType();
|
||||
QString error;
|
||||
};
|
||||
|
||||
@ -140,15 +149,15 @@ public:
|
||||
private:
|
||||
void ensurePanelCreated();
|
||||
|
||||
void editScope(int index, int documentIndex);
|
||||
void editScope(int index, base::optional<int> documentIndex);
|
||||
void editWithUpload(int index, int documentIndex);
|
||||
bool editRequiresScanUpload(int index, int documentIndex) const;
|
||||
void startScopeEdit(int index, int documentIndex);
|
||||
int findNonEmptyDocumentIndex(const Scope &scope) const;
|
||||
bool editRequiresScanUpload(
|
||||
int index,
|
||||
base::optional<int> documentIndex) const;
|
||||
void startScopeEdit(int index, base::optional<int> documentIndex);
|
||||
base::optional<int> findBestDocumentIndex(const Scope &scope) const;
|
||||
void requestScopeFilesType(int index);
|
||||
void cancelValueEdit();
|
||||
std::map<FileType, ScanInfo> valueSpecialFiles(
|
||||
const Value &value) const;
|
||||
void processValueSaveFinished(not_null<const Value*> value);
|
||||
void processVerificationNeeded(not_null<const Value*> value);
|
||||
|
||||
|
@ -330,6 +330,7 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
|
||||
object_ptr<EditScans>(
|
||||
inner,
|
||||
_controller,
|
||||
_scheme.scansHeader,
|
||||
*scansError,
|
||||
std::move(specialFiles),
|
||||
std::move(translations)));
|
||||
|
@ -439,6 +439,7 @@ EditScans::EditScans(
|
||||
EditScans::EditScans(
|
||||
QWidget *parent,
|
||||
not_null<PanelController*> controller,
|
||||
const QString &header,
|
||||
const QString &error,
|
||||
std::map<FileType, ScanInfo> &&specialFiles,
|
||||
base::optional<ScanListData> &&translations)
|
||||
@ -448,7 +449,7 @@ EditScans::EditScans(
|
||||
, _content(this)
|
||||
, _scansList(_controller)
|
||||
, _translationsList(_controller, std::move(translations)) {
|
||||
setupSpecialScans(std::move(specialFiles));
|
||||
setupSpecialScans(header, std::move(specialFiles));
|
||||
}
|
||||
|
||||
base::optional<int> EditScans::validateGetErrorTop() {
|
||||
@ -481,7 +482,7 @@ base::optional<int> EditScans::validateGetErrorTop() {
|
||||
|| scan.file.deleted
|
||||
|| !scan.file.error.isEmpty()) {
|
||||
toggleSpecialScanError(type, true);
|
||||
suggestResult(scan.header->y());
|
||||
suggestResult(scan.header ? scan.header->y() : scan.wrap->y());
|
||||
}
|
||||
}
|
||||
suggestList(FileType::Translation);
|
||||
@ -592,7 +593,9 @@ void EditScans::setupList(
|
||||
st::passportFormDividerHeight));
|
||||
}
|
||||
|
||||
void EditScans::setupSpecialScans(std::map<FileType, ScanInfo> &&files) {
|
||||
void EditScans::setupSpecialScans(
|
||||
const QString &header,
|
||||
std::map<FileType, ScanInfo> &&files) {
|
||||
const auto requiresBothSides = files.find(FileType::ReverseSide)
|
||||
!= end(files);
|
||||
const auto title = [&](FileType type) {
|
||||
@ -665,16 +668,18 @@ void EditScans::setupSpecialScans(std::map<FileType, ScanInfo> &&files) {
|
||||
SpecialScan(std::move(info))).first;
|
||||
auto &scan = i->second;
|
||||
|
||||
scan.header = inner->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
|
||||
inner,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
if (_specialScans.size() == 1) {
|
||||
scan.header = inner->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
|
||||
inner,
|
||||
title(type),
|
||||
Ui::FlatLabel::InitType::Simple,
|
||||
st::passportFormHeader),
|
||||
st::passportUploadHeaderPadding));
|
||||
scan.header->toggle(scan.file.key.id != 0, anim::type::instant);
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
inner,
|
||||
header,
|
||||
Ui::FlatLabel::InitType::Simple,
|
||||
st::passportFormHeader),
|
||||
st::passportUploadHeaderPadding));
|
||||
scan.header->toggle(scan.file.key.id != 0, anim::type::instant);
|
||||
}
|
||||
scan.wrap = inner->add(object_ptr<Ui::VerticalLayout>(inner));
|
||||
if (scan.file.key.id) {
|
||||
createSpecialScanRow(scan, scan.file, requiresBothSides);
|
||||
@ -787,7 +792,9 @@ void EditScans::updateSpecialScan(ScanInfo &&info) {
|
||||
createSpecialScanRow(scan, info, requiresBothSides);
|
||||
scan.wrap->resizeToWidth(width());
|
||||
scan.row->show(anim::type::normal);
|
||||
scan.header->show(anim::type::normal);
|
||||
if (scan.header) {
|
||||
scan.header->show(anim::type::normal);
|
||||
}
|
||||
specialScanChanged(type, true);
|
||||
}
|
||||
scan.file = std::move(info);
|
||||
@ -805,12 +812,12 @@ void EditScans::createSpecialScanRow(
|
||||
switch (type) {
|
||||
case FileType::FrontSide:
|
||||
return lang(requiresBothSides
|
||||
? lng_passport_front_side_name
|
||||
: lng_passport_main_page_name);
|
||||
? lng_passport_front_side_title
|
||||
: lng_passport_main_page_title);
|
||||
case FileType::ReverseSide:
|
||||
return lang(lng_passport_reverse_side_name);
|
||||
return lang(lng_passport_reverse_side_title);
|
||||
case FileType::Selfie:
|
||||
return lang(lng_passport_selfie_name);
|
||||
return lang(lng_passport_selfie_title);
|
||||
}
|
||||
Unexpected("Type in special file name.");
|
||||
}();
|
||||
|
@ -55,6 +55,7 @@ public:
|
||||
EditScans(
|
||||
QWidget *parent,
|
||||
not_null<PanelController*> controller,
|
||||
const QString &header,
|
||||
const QString &error,
|
||||
std::map<FileType, ScanInfo> &&specialFiles,
|
||||
base::optional<ScanListData> &&translations);
|
||||
@ -112,7 +113,9 @@ private:
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
FileType type,
|
||||
const QString &header);
|
||||
void setupSpecialScans(std::map<FileType, ScanInfo> &&files);
|
||||
void setupSpecialScans(
|
||||
const QString &header,
|
||||
std::map<FileType, ScanInfo> &&files);
|
||||
void init();
|
||||
|
||||
void chooseScan(FileType type);
|
||||
|
Loading…
Reference in New Issue
Block a user