diff --git a/src/plugins/preferences/CMakeLists.txt b/src/plugins/preferences/CMakeLists.txt index 61b60c92c..ab355a0fb 100644 --- a/src/plugins/preferences/CMakeLists.txt +++ b/src/plugins/preferences/CMakeLists.txt @@ -196,6 +196,9 @@ set(HEADERS common/preferencestreeproxymodel.h common/preferenceswidget.h + + common/inputmessagenotifier.h + common/inputdetectors.h ) set(SOURCES @@ -378,6 +381,9 @@ set(SOURCES common/preferencestreeproxymodel.cpp common/preferenceswidget.cpp + + common/inputmessagenotifier.cpp + common/inputdetectors.cpp ) set(UI_FORMS diff --git a/src/plugins/preferences/common/i18n/preferences_common_translation_en.ts b/src/plugins/preferences/common/i18n/preferences_common_translation_en.ts index 63b6e6f58..3a06657eb 100644 --- a/src/plugins/preferences/common/i18n/preferences_common_translation_en.ts +++ b/src/plugins/preferences/common/i18n/preferences_common_translation_en.ts @@ -764,6 +764,37 @@ context (user policy option) Common + + preferences::InputMessageNotifier + + The input field has a space at the beginning or at the end + The input field has a space at the beginning or at the end + + + There is a folder '.'/'..' in the path input field + There is a folder '.'/'..' in the path input field + + + The path input field contains the path to the folder + The path input field contains the path to the folder + + + The path input field contains a path that is not compatible with windows + The path input field contains a path that is not compatible with windows + + + The path input field contains a relative path + The path input field contains a relative path + + + The path input field contains a path from root + The path input field contains a path from root + + + The path input field contains a network path + The path input field contains a network path + + preferences::PreferencesTreeView diff --git a/src/plugins/preferences/common/i18n/preferences_common_translation_ru.ts b/src/plugins/preferences/common/i18n/preferences_common_translation_ru.ts index 3f6734b73..12e58cd79 100644 --- a/src/plugins/preferences/common/i18n/preferences_common_translation_ru.ts +++ b/src/plugins/preferences/common/i18n/preferences_common_translation_ru.ts @@ -765,6 +765,37 @@ context (user policy option) Общие + + preferences::InputMessageNotifier + + The input field has a space at the beginning or at the end + Поле ввода содержит пробел в начале или в конце + + + There is a folder '.'/'..' in the path input field + В поле ввода пути есть папка "."/".." + + + The path input field contains the path to the folder + Поле ввода пути содержит путь к папке + + + The path input field contains a path that is not compatible with windows + Поле ввода path содержит путь, который несовместим с Windows + + + The path input field contains a relative path + Поле ввода path содержит относительный путь + + + The path input field contains a path from root + Поле ввода path содержит путь от корня + + + The path input field contains a network path + Поле ввода path содержит сетевой путь + + preferences::PreferencesTreeView diff --git a/src/plugins/preferences/common/inputdetectors.cpp b/src/plugins/preferences/common/inputdetectors.cpp new file mode 100644 index 000000000..80f7ff337 --- /dev/null +++ b/src/plugins/preferences/common/inputdetectors.cpp @@ -0,0 +1,15 @@ +#include "inputdetectors.h" + +namespace preferences { + +bool WhitespaceDetector::detect(const QString &input) +{ + return input.trimmed() != input; +} + +bool EmptyDetector::detect(const QString &input) +{ + return input.length() == 0; +} + +} // namespace preferences diff --git a/src/plugins/preferences/common/inputdetectors.h b/src/plugins/preferences/common/inputdetectors.h new file mode 100644 index 000000000..d66a0382c --- /dev/null +++ b/src/plugins/preferences/common/inputdetectors.h @@ -0,0 +1,42 @@ +/*********************************************************************************************************************** +** +** Copyright (C) 2021 BaseALT Ltd. +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +***********************************************************************************************************************/ + +#ifndef GPUI_INPUTDETECTORS_H +#define GPUI_INPUTDETECTORS_H + +#include "inputmessagenotifier.h" + +namespace preferences { + +class WhitespaceDetector : public InputDetector +{ +public: + bool detect(const QString &input) override; +}; + +class EmptyDetector : public InputDetector +{ +public: + bool detect(const QString &input) override; +}; + +} // namespace preferences + +#endif // GPUI_INPUTDETECTORS_H diff --git a/src/plugins/preferences/common/inputmessagenotifier.cpp b/src/plugins/preferences/common/inputmessagenotifier.cpp new file mode 100644 index 000000000..8c3769d3e --- /dev/null +++ b/src/plugins/preferences/common/inputmessagenotifier.cpp @@ -0,0 +1,206 @@ +/*********************************************************************************************************************** +** +** Copyright (C) 2021 BaseALT Ltd. +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +***********************************************************************************************************************/ + +#include "inputmessagenotifier.h" +#include +#include + +namespace preferences { + +InputMessageNotifier::DetectElement::DetectElement(QWidget *parent, QLayout *layout, + const QString &message, + InputMessageNotifier::MessageLevel level) +{ + this->m_label = new QLabel(parent); + layout->addWidget(this->m_label); + + this->m_label->hide(); + + this->m_label->setText(message); + + switch (level) { + case InputMessageNotifier::MessageLevel::Warning: + this->m_label->setStyleSheet("background-color: rgb(249, 240, 107);\n" + "color: rgb(0,0,0);\n" + "border-radius: 10px;\n" + "padding: 10px;"); + break; + + default: + case InputMessageNotifier::MessageLevel::Error: + this->m_label->setStyleSheet("background-color: rgb(246, 97, 81);\n" + "color: rgb(255, 255, 255);\n" + "border-radius: 10px;\n" + "padding: 10px;"); + break; + } + + this->m_level = level; +} +InputMessageNotifier::DetectElement::DetectElement(DetectElement &&element) +{ + this->m_detected = element.m_detected; + this->m_label = element.m_label; + this->m_level = element.m_level; + element.m_label = nullptr; +} +InputMessageNotifier::DetectElement & +InputMessageNotifier::DetectElement::operator=(DetectElement &&element) +{ + if (this->m_label) { + delete this->m_label; + } + + this->m_detected = element.m_detected; + this->m_label = element.m_label; + this->m_level = element.m_level; + element.m_label = nullptr; + + return *this; +} + +void InputMessageNotifier::DetectElement::detect() +{ + this->m_label->show(); + this->m_detected = true; +} +void InputMessageNotifier::DetectElement::undetect() +{ + this->m_label->hide(); + this->m_detected = false; +} + +bool InputMessageNotifier::DetectElement::detected() +{ + return this->m_detected; +} + +InputMessageNotifier::MessageLevel InputMessageNotifier::DetectElement::level() +{ + return this->m_level; +} + +void InputMessageNotifier::DetectElement::setMessage(const QString &message) +{ + this->m_label->setText(message); +} + +InputMessageNotifier::DetectElement::~DetectElement() +{ + if (this->m_label) { + delete this->m_label; + } +} + +InputMessageNotifier::InputMessageNotifier(QWidget *widget) : QWidget(widget) +{ + this->m_layout = new QVBoxLayout; + this->setLayout(this->m_layout); +} + +size_t InputMessageNotifier::addDetector(std::unique_ptr detector) +{ + this->m_detectors[this->m_nextDetector] = std::move(detector); + return this->m_nextDetector++; +} + +void InputMessageNotifier::removeDetector(size_t id) +{ + this->m_detectors.erase(id); +} + +void InputMessageNotifier::addInput(const QString &name) +{ + this->m_instances[name] = std::move(InputInstance()); +} + +void InputMessageNotifier::removeInput(const QString &name) +{ + this->m_instances.erase(name); +} + +void InputMessageNotifier::updateInput(const QString &name, const QString &input) +{ + auto &instance = this->m_instances[name]; + + for (auto &detect : instance) { + bool detected = this->m_detectors[detect.first]->detect(input); + + if (detected && !detect.second.detected()) { + detect.second.detect(); + this->incCounter(detect.second.level()); + } else if (!detected && detect.second.detected()) { + detect.second.undetect(); + this->decCounter(detect.second.level()); + } + } +} + +void InputMessageNotifier::setMessage(const QString &name, size_t detector, const QString &message) +{ + auto entry = this->m_instances[name].find(detector); + if (entry != this->m_instances[name].end()) { + entry->second.setMessage(message); + } +} + +void InputMessageNotifier::attachDetector(const QString &name, size_t detector, + const QString &message, MessageLevel level) +{ + this->m_instances[name].insert(std::pair( + detector, DetectElement(this, this->m_layout, message, level))); +} + +bool InputMessageNotifier::hasAnyError() +{ + return this->m_errorCount != 0; +} + +bool InputMessageNotifier::hasAnyWarning() +{ + return this->m_warningCount != 0; +} + +InputMessageNotifier::~InputMessageNotifier() { } + +void InputMessageNotifier::incCounter(InputMessageNotifier::MessageLevel level) +{ + switch (level) { + case InputMessageNotifier::MessageLevel::Warning: + ++this->m_warningCount; + break; + case InputMessageNotifier::MessageLevel::Error: + ++this->m_errorCount; + break; + } +} +void InputMessageNotifier::decCounter(MessageLevel level) +{ + switch (level) { + case InputMessageNotifier::MessageLevel::Warning: + --this->m_warningCount; + break; + case InputMessageNotifier::MessageLevel::Error: + --this->m_errorCount; + break; + } +} + +} // namespace preferences diff --git a/src/plugins/preferences/common/inputmessagenotifier.h b/src/plugins/preferences/common/inputmessagenotifier.h new file mode 100644 index 000000000..23b06a7a9 --- /dev/null +++ b/src/plugins/preferences/common/inputmessagenotifier.h @@ -0,0 +1,164 @@ +/*********************************************************************************************************************** +** +** Copyright (C) 2021 BaseALT Ltd. +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +***********************************************************************************************************************/ + +#ifndef GPUI_INPUTMESSAGENOTIFIER_H +#define GPUI_INPUTMESSAGENOTIFIER_H + +#include +#include +#include + +namespace preferences { + +class InputDetector +{ +public: + virtual bool detect(const QString &input) = 0; + virtual ~InputDetector() = default; +}; + +class InputMessageNotifier : public QWidget +{ +public: + Q_OBJECT + +public: + enum class MessageLevel { + Warning, + Error, + }; + +private: + typedef struct DetectElement + { + public: + DetectElement(QWidget *parent, QLayout *layout, const QString &message, MessageLevel level); + DetectElement(DetectElement &&element); + DetectElement &operator=(DetectElement &&element); + + void detect(); + void undetect(); + + bool detected(); + MessageLevel level(); + void setMessage(const QString &message); + + ~DetectElement(); + + private: + DetectElement(const DetectElement &) = delete; // copy ctor + DetectElement &operator=(const DetectElement &) = delete; // copy assignment + + private: + bool m_detected{ false }; + QLabel *m_label{ nullptr }; + MessageLevel m_level{ MessageLevel::Warning }; + } DetectElement; + + typedef std::unordered_map InputInstance; + +public: + InputMessageNotifier(QWidget *widget); + + /** + * @brief add detector. + * @param detector Detector. + * @return Detector id. + */ + size_t addDetector(std::unique_ptr detector); + + /** + * @brief remove detector. + * @param id Detector id. + */ + void removeDetector(size_t id); + + /** + * @brief registrate input. + * @param name Name of input to be registrated. + */ + void addInput(const QString &name); + + /** + * @brief unregistrate input. + * @param name Name of input to be unregistrated. + */ + void removeInput(const QString &name); + + /** + * @brief update input content and validate it. + * @param name Name of registrated input. + * @param input Input content. + */ + void updateInput(const QString &name, const QString &input); + + /** + * @brief update input content and validate it. + * @param name Name of registrated input. + * @param detector Detector id. + * @param message MessageNotifier message on detect. + */ + void setMessage(const QString &name, size_t detector, const QString &message); + + /** + * @brief attach detector to input. + * @param name Name of registrated input. + * @param detector Detector id. + * @param level Message level(visual and see hasAnyError() and hasAnyWarning()) + */ + void attachDetector(const QString &name, size_t detector, const QString &message, + MessageLevel level = MessageLevel::Warning); + + /** + * @return true, if any error has been detected(on any input). + */ + bool hasAnyError(); + + /** + * @return true, if any warning has been detected(on any input). + */ + bool hasAnyWarning(); + + ~InputMessageNotifier(); + +private: + InputMessageNotifier(const InputMessageNotifier &) = delete; // copy ctor + InputMessageNotifier(InputMessageNotifier &&) = delete; // move ctor + InputMessageNotifier &operator=(const InputMessageNotifier &) = delete; // copy assignment + InputMessageNotifier &operator=(InputMessageNotifier &&) = delete; // move assignment + + void incCounter(MessageLevel level); + void decCounter(MessageLevel level); + +private: + std::unordered_map> m_detectors{}; + std::unordered_map m_instances{}; + + size_t m_errorCount{ 0 }; + size_t m_warningCount{ 0 }; + + size_t m_nextDetector{ 0 }; + + QLayout *m_layout{ nullptr }; +}; + +} // namespace preferences + +#endif // GPUI_INPUTMESSAGENOTIFIER_H diff --git a/src/plugins/preferences/files/fileswidget.cpp b/src/plugins/preferences/files/fileswidget.cpp index 3c1b71e82..6490edbe0 100644 --- a/src/plugins/preferences/files/fileswidget.cpp +++ b/src/plugins/preferences/files/fileswidget.cpp @@ -20,6 +20,7 @@ #include "fileswidget.h" #include "ui_fileswidget.h" +#include "common/inputdetectors.h" #include "common/commonutils.h" #include "filesitem.h" @@ -40,6 +41,20 @@ FilesWidget::FilesWidget(QWidget *parent, FilesItem *item) { ui->setupUi(this); + this->whitespaceDetector = ui->inputMessage->addDetector(std::unique_ptr(new WhitespaceDetector)); + this->emptyDetector = ui->inputMessage->addDetector(std::unique_ptr(new EmptyDetector)); + + ui->inputMessage->addInput("source_file"); + ui->inputMessage->addInput("destination"); + + ui->inputMessage->attachDetector("source_file", whitespaceDetector, tr("source_file_whitespace")); + ui->inputMessage->attachDetector("source_file", emptyDetector, tr("source_file_empty"), InputMessageNotifier::MessageLevel::Error); + + ui->inputMessage->attachDetector("destination", whitespaceDetector, tr("destination_whitespace")); + ui->inputMessage->attachDetector("destination", emptyDetector, tr("destination_empty"), InputMessageNotifier::MessageLevel::Error); + + ui->sourceLineEdit->setToolTip("test"); + on_actionComboBox_currentIndexChanged(ui->actionComboBox->currentIndex()); } diff --git a/src/plugins/preferences/files/fileswidget.h b/src/plugins/preferences/files/fileswidget.h index 5a558a5d9..e7593134f 100644 --- a/src/plugins/preferences/files/fileswidget.h +++ b/src/plugins/preferences/files/fileswidget.h @@ -65,6 +65,7 @@ private slots: void on_actionComboBox_currentIndexChanged(int index); void on_destinationToolButton_clicked(); void on_sourceLineEdit_textChanged(const QString &text); + void on_destinationLineEdit_textChanged(const QString &text); void on_sourceToolButton_clicked(); private: @@ -82,6 +83,9 @@ private slots: bool fileMode {true}; + size_t whitespaceDetector{0}; + size_t emptyDetector{0}; + private: Ui::FilesWidget *ui {nullptr}; }; diff --git a/src/plugins/preferences/files/fileswidget.ui b/src/plugins/preferences/files/fileswidget.ui index fe4c13970..b8c814c7c 100644 --- a/src/plugins/preferences/files/fileswidget.ui +++ b/src/plugins/preferences/files/fileswidget.ui @@ -117,6 +117,9 @@ + + + @@ -202,6 +205,12 @@ QLineEdit
common/shortcutlineedit.h
+ + preferences::InputMessageNotifier + QWidget +
common/inputmessagenotifier.h
+ 1 +
diff --git a/src/plugins/preferences/files/fileswidgetslots.cpp b/src/plugins/preferences/files/fileswidgetslots.cpp index 89c9aa5ae..41796dadf 100644 --- a/src/plugins/preferences/files/fileswidgetslots.cpp +++ b/src/plugins/preferences/files/fileswidgetslots.cpp @@ -103,6 +103,11 @@ void FilesWidget::on_destinationToolButton_clicked() } } +void FilesWidget::on_destinationLineEdit_textChanged(const QString &text) +{ + ui->inputMessage->updateInput("destination", text); +} + void FilesWidget::on_sourceLineEdit_textChanged(const QString &text) { if (text.contains('*') || text.contains('?')) @@ -110,13 +115,18 @@ void FilesWidget::on_sourceLineEdit_textChanged(const QString &text) fileMode = false; ui->destinationLabel->setText(tr("Destination folder:")); + ui->inputMessage->setMessage("destination", this->whitespaceDetector, tr("destination_folder_whitespace")); + ui->inputMessage->setMessage("destination", this->emptyDetector, tr("destination_folder_empty")); } else { fileMode = true; ui->destinationLabel->setText(tr("Destination file:")); + ui->inputMessage->setMessage("destination", this->whitespaceDetector, tr("destination_whitespace")); + ui->inputMessage->setMessage("destination", this->emptyDetector, tr("destination_empty")); } + ui->inputMessage->updateInput("source_file", text); } } // namespace preferences diff --git a/src/plugins/preferences/files/i18n/files_translation_en.ts b/src/plugins/preferences/files/i18n/files_translation_en.ts index 482879400..1f6e5f3f4 100644 --- a/src/plugins/preferences/files/i18n/files_translation_en.ts +++ b/src/plugins/preferences/files/i18n/files_translation_en.ts @@ -57,32 +57,32 @@ Destination:
- + Supress errors on individual file actions Supress errors on individual file actions - + Attributes Attributes - + Read-only Read-only - + Hidden Hidden - + Archive Archive - + Executable Executable @@ -120,13 +120,13 @@ Target - - + + All files (*) All files (*) - + All files (*.*) All files (*.*) @@ -134,17 +134,39 @@ preferences::FilesWidget - + + + source_file_whitespace + The input field of the source file contains a space at the beginning or at the end + + + + destination_whitespace + The input field of the destination file contains a space at the beginning or at the end + + + + source_file_empty + An empty input field for the source file + + + + + destination_empty + An empty input field for the destination file + + + Please enter source file(s) value. Please enter source file(s) value. - + Please enter destination file(s) value. Please enter destination file(s) value. - + General General @@ -155,10 +177,20 @@ - + Destination file: Destination file: + + + destination_folder_whitespace + The input field of the destination folder contains a space at the beginning or at the end + + + + destination_folder_empty + An empty input field for the destination folder + Open Open @@ -184,7 +216,7 @@ Open Directory - + Destination folder: Destination folder: diff --git a/src/plugins/preferences/files/i18n/files_translation_ru.ts b/src/plugins/preferences/files/i18n/files_translation_ru.ts index e0ab08f8c..4f46080c4 100644 --- a/src/plugins/preferences/files/i18n/files_translation_ru.ts +++ b/src/plugins/preferences/files/i18n/files_translation_ru.ts @@ -57,32 +57,32 @@ Назначение: - + Supress errors on individual file actions Подавление ошибок при действиях с отдельными файлами - + Attributes Атрибуты - + Read-only Только для чтения - + Hidden Скрытый - + Archive Архивный - + Executable Исполняемый @@ -120,13 +120,13 @@ Цель - - + + All files (*) Все файлы (*) - + All files (*.*) Все файлы (*.*) @@ -134,17 +134,39 @@ preferences::FilesWidget - + + + source_file_whitespace + Поле ввода исходного файла содержит пробел в начале или в конце + + + + destination_whitespace + Поле ввода целевого файла содержит пробел в начале или в конце + + + + source_file_empty + Пустое поле ввода исходного файла + + + + + destination_empty + Пустое поле ввода файла назначения + + + Please enter source file(s) value. Пожалуйста, введите источник файла(ов). - + Please enter destination file(s) value. Пожалуйста, введите место назначения файла(ов). - + General Основные настройки @@ -155,10 +177,20 @@ - + Destination file: Место назначения файлов: + + + destination_folder_whitespace + Поле ввода целевой папки содержит пробел в начале или в конце + + + + destination_folder_empty + Пустое поле ввода папки назначения + Open Открыть @@ -184,7 +216,7 @@ Открыть Директорию - + Destination folder: Папка назначения: