File indexing completed on 2024-05-05 17:43:14
0001 /* 0002 * SPDX-FileCopyrightText: 2017 Ivan Cukic <ivan.cukic (at) kde.org> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #ifndef PLASMAVAULT_KDED_UI_VAULT_WIZARD_BASE_P_H 0008 #define PLASMAVAULT_KDED_UI_VAULT_WIZARD_BASE_P_H 0009 0010 #include <QPushButton> 0011 0012 namespace PlasmaVault 0013 { 0014 class Vault; 0015 } // namespace PlasmaVault 0016 0017 #define WBASE(Class) VaultWizardBase<Class, Ui::Class, Class::Private> 0018 0019 template<typename Class, typename Ui, typename Impl> 0020 class VaultWizardBase 0021 { 0022 public: 0023 Class *const q; 0024 Ui ui; 0025 0026 QPushButton *buttonPrevious; 0027 QPushButton *buttonNext; 0028 QStackedLayout *layout; 0029 0030 bool lastModule = false; 0031 QString lastButtonText; 0032 inline void setLastModule(bool last) 0033 { 0034 lastModule = last; 0035 if (last) { 0036 buttonNext->setText(lastButtonText); 0037 buttonNext->setIcon(QIcon::fromTheme("dialog-ok-apply")); 0038 } else { 0039 buttonNext->setText(i18n("Next")); 0040 buttonNext->setIcon(QIcon::fromTheme("go-next")); 0041 } 0042 } 0043 0044 QVector<DialogDsl::DialogModule *> currentStepModules; 0045 steps currentSteps; 0046 BackendChooserWidget *firstStepModule = nullptr; 0047 DialogDsl::DialogModule *currentModule = nullptr; 0048 0049 // to suggest the highest priority to the user as a starting value 0050 QMap<QString, int> priorities = { 0051 {"gocryptfs", 1}, 0052 {"encfs", 2}, 0053 {"cryfs", 3}, 0054 }; 0055 0056 template<typename ClickHandler> 0057 QPushButton *addDialogButton(const QString &icon, const QString &title, ClickHandler clickHandler) 0058 { 0059 auto button = new QPushButton(QIcon::fromTheme(icon), title); 0060 ui.buttons->addButton(button, QDialogButtonBox::ActionRole); 0061 QObject::connect(button, &QPushButton::clicked, q, clickHandler); 0062 return button; 0063 } 0064 0065 Impl *self() 0066 { 0067 return static_cast<Impl *>(this); 0068 } 0069 0070 VaultWizardBase(Class *parent) 0071 : q(parent) 0072 { 0073 ui.setupUi(parent); 0074 ui.message->hide(); 0075 0076 layout = new QStackedLayout(); 0077 layout->setContentsMargins(0, 0, 0, 0); 0078 ui.container->setLayout(layout); 0079 0080 lastButtonText = i18n("Create"); 0081 } 0082 0083 void initBase() 0084 { 0085 // The dialog buttons do not have previous/next by default 0086 // so we need to create them 0087 buttonPrevious = addDialogButton("go-previous", i18n("Previous"), [this] { 0088 previousStep(); 0089 }); 0090 buttonNext = addDialogButton("go-next", i18n("Next"), [this] { 0091 if (lastModule) 0092 self()->finish(); 0093 else 0094 nextStep(); 0095 }); 0096 0097 // The 'Create' button should be hidden by default 0098 buttonPrevious->setEnabled(false); 0099 buttonNext->setEnabled(false); 0100 buttonNext->setDefault(true); 0101 0102 // Loading the fist page of the wizard 0103 firstStepModule = new BackendChooserWidget(); 0104 setCurrentModule(firstStepModule); 0105 layout->addWidget(firstStepModule); 0106 0107 // Loading the backends to the combo box 0108 for (const auto &key : self()->logic.keys()) { 0109 firstStepModule->addItem(key, key.translation(), priorities.value(key)); 0110 } 0111 firstStepModule->checkBackendAvailable(); 0112 } 0113 0114 void setCurrentModule(DialogDsl::DialogModule *module) 0115 { 0116 // If there is a current module already, disconnect it 0117 if (currentModule) { 0118 currentModule->aboutToBeHidden(); 0119 currentModule->disconnect(); 0120 } 0121 0122 // The current module needs to be changed 0123 currentModule = module; 0124 currentModule->aboutToBeShown(); 0125 0126 QObject::connect(currentModule, &DialogModule::isValidChanged, q, [&](bool valid) { 0127 buttonNext->setEnabled(valid); 0128 }); 0129 0130 // Lets update the button states 0131 0132 // 1. next/create button is enabled only if the current 0133 // module is in the valid state 0134 buttonNext->setEnabled(currentModule->isValid()); 0135 0136 // 2. previous button is enabled only if we are not on 0137 // the first page 0138 buttonPrevious->setEnabled(currentStepModules.size() > 0); 0139 0140 // 3. If we have loaded the last page, we want to show the 0141 // 'Create' button instead of 'Next' 0142 setLastModule(!currentSteps.isEmpty() && currentStepModules.size() == currentSteps.size()); 0143 0144 // Calling to initialize the module -- we are passing all the 0145 // previously collected data to it 0146 auto collectedPayload = firstStepModule == module ? PlasmaVault::Vault::Payload{} : firstStepModule->fields(); 0147 for (const auto *module : currentStepModules) { 0148 collectedPayload.insert(module->fields()); 0149 } 0150 currentModule->init(collectedPayload); 0151 } 0152 0153 void previousStep() 0154 { 0155 if (currentStepModules.isEmpty()) 0156 return; 0157 0158 // We want to kill the current module, and move to the previous one 0159 currentStepModules.takeLast(); 0160 currentModule->deleteLater(); 0161 ; 0162 0163 if (currentStepModules.size()) { 0164 setCurrentModule(currentStepModules.last()); 0165 } else { 0166 setCurrentModule(firstStepModule); 0167 } 0168 0169 if (!currentModule->shouldBeShown()) { 0170 previousStep(); 0171 } 0172 } 0173 0174 void nextStep() 0175 { 0176 // If the current module is not filled properly, do 0177 // not go to the next step 0178 if (currentModule && !currentModule->isValid()) { 0179 return; 0180 } 0181 0182 // If the step modules are empty, this means that we 0183 // have just started - the user chose the backend 0184 // and we need to load the vault creation steps 0185 if (currentStepModules.isEmpty()) { 0186 const auto &fields = firstStepModule->fields(); 0187 currentSteps = self()->logic[fields[KEY_BACKEND].toByteArray()]; 0188 } 0189 0190 // Loading the modules that we need to show now 0191 auto subModules = currentSteps[currentStepModules.size()]; 0192 0193 // If there is only one module on the current page, 0194 // lets not complicate things by creating the compound module 0195 DialogModule *stepWidget = (subModules.size() == 1) ? subModules.first()() : new CompoundDialogModule(subModules); 0196 0197 // Adding the widget to the list and the layout 0198 currentStepModules << stepWidget; 0199 layout->addWidget(stepWidget); 0200 layout->setCurrentWidget(stepWidget); 0201 0202 // Set the newly added module to be the current 0203 setCurrentModule(stepWidget); 0204 0205 if (!currentModule->shouldBeShown()) { 0206 nextStep(); 0207 } 0208 } 0209 }; 0210 0211 #endif // include guard