File indexing completed on 2024-06-16 04:47:18
0001 /*************************************************************************** 0002 * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr 0003 * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 ***************************************************************************/ 0006 /** @file 0007 * This file is a plugin for undo and redo operation. 0008 * 0009 * @author Stephane MANKOWSKI / Guillaume DE BURE 0010 */ 0011 #include "skgundoredoplugin.h" 0012 0013 #include <qwidget.h> 0014 0015 #include <kaboutdata.h> 0016 #include <kactioncollection.h> 0017 #include <kpluginfactory.h> 0018 #include <kstandardaction.h> 0019 #include <ktoolbarpopupaction.h> 0020 0021 #include "skgerror.h" 0022 #include "skgmainpanel.h" 0023 #include "skgservices.h" 0024 #include "skgtraces.h" 0025 #include "skgundoredo_settings.h" 0026 #include "skgundoredoplugindockwidget.h" 0027 0028 /** 0029 * This plugin factory. 0030 */ 0031 K_PLUGIN_CLASS_WITH_JSON(SKGUndoRedoPlugin, "metadata.json") 0032 0033 SKGUndoRedoPlugin::SKGUndoRedoPlugin(QWidget* iWidget, QObject* iParent, const QVariantList& /*iArg*/) : 0034 SKGInterfacePlugin(iParent), 0035 m_undoSaveAction(nullptr), m_undoAction(nullptr), m_redoAction(nullptr), m_undoMenu(nullptr), m_redoMenu(nullptr), m_currentDocument(nullptr), m_dockWidget(nullptr) 0036 { 0037 Q_UNUSED(iWidget) 0038 SKGTRACEINFUNC(10) 0039 } 0040 0041 SKGUndoRedoPlugin::~SKGUndoRedoPlugin() 0042 { 0043 SKGTRACEINFUNC(10) 0044 m_currentDocument = nullptr; 0045 m_dockWidget = nullptr; 0046 m_undoSaveAction = nullptr; 0047 m_undoAction = nullptr; 0048 m_redoAction = nullptr; 0049 m_undoMenu = nullptr; 0050 m_redoMenu = nullptr; 0051 } 0052 0053 bool SKGUndoRedoPlugin::setupActions(SKGDocument* iDocument) 0054 { 0055 SKGTRACEINFUNC(10) 0056 0057 m_currentDocument = iDocument; 0058 0059 setComponentName(QStringLiteral("skg_undoredo"), title()); 0060 setXMLFile(QStringLiteral("skg_undoredo.rc")); 0061 0062 // Menu 0063 m_undoSaveAction = new QAction(SKGServices::fromTheme(QStringLiteral("document-revert")), i18nc("Verb, action to cancel previous action", "Revert document"), this); 0064 connect(m_undoSaveAction, &QAction::triggered, this, &SKGUndoRedoPlugin::onUndoSave); 0065 actionCollection()->setDefaultShortcut(m_undoSaveAction, Qt::CTRL + Qt::ALT + Qt::Key_Z); 0066 registerGlobalAction(QStringLiteral("edit_undolastsave"), m_undoSaveAction); 0067 0068 m_undoAction = new KToolBarPopupAction(SKGServices::fromTheme(QStringLiteral("edit-undo")), i18nc("Verb, action to cancel previous action", "Undo"), this); 0069 connect(m_undoAction, &KToolBarPopupAction::triggered, this, &SKGUndoRedoPlugin::onUndo); 0070 actionCollection()->setDefaultShortcut(m_undoAction, Qt::CTRL + Qt::Key_Z); 0071 m_undoAction->setPriority(QAction::LowPriority); 0072 m_undoMenu = m_undoAction->menu(); 0073 connect(m_undoMenu, &QMenu::aboutToShow, this, &SKGUndoRedoPlugin::onShowUndoMenu); 0074 m_undoAction->setStickyMenu(false); 0075 m_undoAction->setData(1); 0076 registerGlobalAction(QStringLiteral("edit_undo"), m_undoAction); 0077 0078 m_redoAction = new KToolBarPopupAction(SKGServices::fromTheme(QStringLiteral("edit-redo")), i18nc("Verb, action to redo previous cancelled action", "Redo"), this); 0079 connect(m_redoAction, &KToolBarPopupAction::triggered, this, &SKGUndoRedoPlugin::onRedo); 0080 actionCollection()->setDefaultShortcut(m_redoAction, Qt::CTRL + Qt::SHIFT + Qt::Key_Z); 0081 m_redoAction->setPriority(QAction::LowPriority); 0082 m_redoMenu = m_redoAction->menu(); 0083 connect(m_redoMenu, &QMenu::aboutToShow, this, &SKGUndoRedoPlugin::onShowRedoMenu); 0084 m_redoAction->setStickyMenu(false); 0085 m_redoAction->setData(1); 0086 registerGlobalAction(QStringLiteral("edit_redo"), m_redoAction); 0087 0088 // Menu 0089 auto act = new QAction(SKGServices::fromTheme(QStringLiteral("edit-clear-history")), i18nc("Verb, action to cancel previous action", "Clear history"), this); 0090 connect(act, &QAction::triggered, this, &SKGUndoRedoPlugin::onClearHistory); 0091 registerGlobalAction(QStringLiteral("edit_clear_history"), act); 0092 0093 // Dock creation 0094 m_dockWidget = new QDockWidget(SKGMainPanel::getMainPanel()); 0095 m_dockWidget->setObjectName(QStringLiteral("skg_undoredo_docwidget")); 0096 m_dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); 0097 m_dockWidget->setWindowTitle(title()); 0098 0099 // add action to control hide / display of history 0100 QAction* toggle = m_dockWidget->toggleViewAction(); 0101 QAction* panelAction = actionCollection()->addAction(QStringLiteral("view_transactions")); 0102 registerGlobalAction(QStringLiteral("view_transactions"), panelAction); 0103 panelAction->setCheckable(true); 0104 panelAction->setChecked(toggle->isChecked()); 0105 panelAction->setText(toggle->text()); 0106 actionCollection()->setDefaultShortcut(panelAction, Qt::SHIFT + Qt::Key_F11); 0107 connect(panelAction, &QAction::triggered, toggle, &QAction::trigger); 0108 connect(toggle, &QAction::toggled, panelAction, &QAction::setChecked); 0109 0110 return true; 0111 } 0112 0113 void SKGUndoRedoPlugin::refresh() 0114 { 0115 SKGTRACEINFUNC(10) 0116 0117 if (m_dockWidget->widget() == nullptr) { 0118 auto w = new SKGUndoRedoPluginDockWidget(SKGMainPanel::getMainPanel(), m_currentDocument); 0119 connect(w, &SKGUndoRedoPluginDockWidget::selectionChanged, SKGMainPanel::getMainPanel(), &SKGMainPanel::refresh); 0120 m_dockWidget->setWidget(w); 0121 } 0122 0123 if (m_currentDocument != nullptr) { 0124 bool undoPossible = (m_currentDocument->getNbTransaction(SKGDocument::UNDO) > 0); 0125 if (m_undoSaveAction != nullptr) { 0126 m_undoSaveAction->setEnabled(undoPossible); 0127 } 0128 if (m_undoAction != nullptr) { 0129 m_undoAction->setEnabled(undoPossible); 0130 } 0131 if (m_redoAction != nullptr) { 0132 m_redoAction->setEnabled(m_currentDocument->getNbTransaction(SKGDocument::REDO) > 0); 0133 } 0134 0135 // Refresh undo redo 0136 QString name; 0137 m_currentDocument->getTransactionToProcess(SKGDocument::UNDO, &name); 0138 QString message = i18nc("Verb", "Undo transaction '%1'.", name); 0139 if (name.isEmpty()) { 0140 message = QLatin1String(""); 0141 } 0142 if (m_undoAction != nullptr) { 0143 m_undoAction->setStatusTip(message); 0144 } 0145 0146 m_currentDocument->getTransactionToProcess(SKGDocument::REDO, &name); 0147 message = i18nc("Verb", "Redo transaction '%1'.", name); 0148 if (name.isEmpty()) { 0149 message = QLatin1String(""); 0150 } 0151 if (m_redoAction != nullptr) { 0152 m_redoAction->setStatusTip(message); 0153 } 0154 } 0155 } 0156 0157 void SKGUndoRedoPlugin::initPreferences() 0158 { 0159 // Read Setting 0160 if (m_currentDocument != nullptr) { 0161 KSharedConfigPtr config = KSharedConfig::openConfig(); 0162 KConfigGroup pref = config->group("skg_undoredo"); 0163 pref.writeEntry("maxNumberOfUndo", SKGServices::stringToInt(m_currentDocument->getParameter(QStringLiteral("SKG_UNDO_MAX_DEPTH")))); 0164 pref.writeEntry("cleanHistoryOnSave", (m_currentDocument->getParameter(QStringLiteral("SKG_UNDO_CLEAN_AFTER_SAVE")) == QStringLiteral("Y"))); 0165 0166 skgundoredo_settings::self()->read(); 0167 } 0168 } 0169 0170 0171 QWidget* SKGUndoRedoPlugin::getPreferenceWidget() 0172 { 0173 SKGTRACEINFUNC(10) 0174 // Create widget 0175 auto w = new QWidget(); 0176 ui.setupUi(w); 0177 return w; 0178 } 0179 0180 0181 KConfigSkeleton* SKGUndoRedoPlugin::getPreferenceSkeleton() 0182 { 0183 return skgundoredo_settings::self(); 0184 } 0185 0186 SKGError SKGUndoRedoPlugin::savePreferences() const 0187 { 0188 SKGError err; 0189 if (m_currentDocument != nullptr) { 0190 // Read Setting 0191 QString max = SKGServices::intToString(skgundoredo_settings::maxNumberOfUndo()); 0192 QString clean = (skgundoredo_settings::cleanHistoryOnSave() ? QStringLiteral("Y") : QStringLiteral("N")); 0193 0194 // Save setting in document 0195 if (max != m_currentDocument->getParameter(QStringLiteral("SKG_UNDO_MAX_DEPTH"))) { 0196 err = m_currentDocument->setParameter(QStringLiteral("SKG_UNDO_MAX_DEPTH"), max); 0197 } 0198 if (clean != m_currentDocument->getParameter(QStringLiteral("SKG_UNDO_CLEAN_AFTER_SAVE"))) { 0199 err = m_currentDocument->setParameter(QStringLiteral("SKG_UNDO_CLEAN_AFTER_SAVE"), clean); 0200 } 0201 } 0202 return err; 0203 } 0204 0205 QString SKGUndoRedoPlugin::title() const 0206 { 0207 return i18nc("Noun", "History"); 0208 } 0209 0210 QString SKGUndoRedoPlugin::icon() const 0211 { 0212 return QStringLiteral("edit-undo"); 0213 } 0214 0215 QString SKGUndoRedoPlugin::toolTip() const 0216 { 0217 return i18nc("Noun", "History"); 0218 } 0219 0220 QStringList SKGUndoRedoPlugin::tips() const 0221 { 0222 QStringList output; 0223 output.push_back(i18nc("Description of a tips", "<p>… you can undo and redo your modifications.</p>")); 0224 output.push_back(i18nc("Description of a tips", "<p>… you can modify the maximum size of the undo/redo stack in the <a href=\"skg://tab_configure?page=Undo redo plugin\">settings</a>.</p>")); 0225 return output; 0226 } 0227 0228 int SKGUndoRedoPlugin::getOrder() const 0229 { 0230 return 4; 0231 } 0232 0233 SKGAdviceList SKGUndoRedoPlugin::advice(const QStringList& iIgnoredAdvice) 0234 { 0235 SKGTRACEINFUNC(10) 0236 SKGAdviceList output; 0237 if (!iIgnoredAdvice.contains(QStringLiteral("skgundoredoplugin_too_big"))) { 0238 // Get nb transaction 0239 int nbObject = m_currentDocument->getNbTransaction(); 0240 int priority = qMin(10, nbObject / 50); 0241 if (priority > 0) { 0242 SKGAdvice ad; 0243 ad.setUUID(QStringLiteral("skgundoredoplugin_too_big")); 0244 ad.setPriority(priority); 0245 ad.setShortMessage(i18nc("Advice on making the best (short)", "History is too large")); 0246 ad.setLongMessage(i18nc("Advice on making the best (long)", "You can improve performances by reducing your history size in settings.")); 0247 QStringList autoCorrections; 0248 autoCorrections.push_back(QStringLiteral("skg://edit_clear_history")); 0249 autoCorrections.push_back(i18nc("Advice on making the best (action)", "Open settings panel")); 0250 ad.setAutoCorrections(autoCorrections); 0251 output.push_back(ad); 0252 } 0253 } 0254 0255 return output; 0256 } 0257 0258 SKGError SKGUndoRedoPlugin::executeAdviceCorrection(const QString& iAdviceIdentifier, int iSolution) 0259 { 0260 SKGError err; 0261 if ((m_currentDocument != nullptr) && iAdviceIdentifier == QStringLiteral("skgundoredoplugin_too_big")) { 0262 SKGMainPanel::getMainPanel()->optionsPreferences(this->objectName()); 0263 return err; 0264 } 0265 return SKGInterfacePlugin::executeAdviceCorrection(iAdviceIdentifier, iSolution); 0266 } 0267 0268 void SKGUndoRedoPlugin::onClearHistory() 0269 { 0270 SKGError err; 0271 SKGTRACEINFUNCRC(10, err) 0272 if ((m_currentDocument != nullptr) && (SKGMainPanel::getMainPanel() != nullptr)) { 0273 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 0274 err = m_currentDocument->removeAllTransactions(); 0275 QApplication::restoreOverrideCursor(); 0276 0277 // status bar 0278 IFOKDO(err, SKGError(0, i18nc("Message for successful user action", "Clear history successfully done."))) 0279 else { 0280 err.addError(ERR_FAIL, i18nc("Error message", "Clear history failed")); 0281 } 0282 0283 // Display error 0284 SKGMainPanel::displayErrorMessage(err); 0285 } 0286 } 0287 0288 void SKGUndoRedoPlugin::onUndoSave() 0289 { 0290 SKGError err; 0291 SKGTRACEINFUNCRC(10, err) 0292 if ((m_currentDocument != nullptr) && (SKGMainPanel::getMainPanel() != nullptr)) { 0293 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 0294 err = m_currentDocument->undoRedoTransaction(SKGDocument::UNDOLASTSAVE); 0295 QApplication::restoreOverrideCursor(); 0296 0297 // status bar 0298 IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Undo successfully done."))) 0299 else { 0300 err.addError(ERR_FAIL, i18nc("Error message", "Undo failed")); 0301 } 0302 0303 // Display error 0304 SKGMainPanel::displayErrorMessage(err); 0305 } 0306 } 0307 0308 void SKGUndoRedoPlugin::onUndo() 0309 { 0310 SKGError err; 0311 SKGTRACEINFUNCRC(10, err) 0312 if ((m_currentDocument != nullptr) && (SKGMainPanel::getMainPanel() != nullptr)) { 0313 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 0314 int pos = qobject_cast<QAction*>(sender())->data().toInt(); 0315 for (int i = 1 ; !err && i <= pos; ++i) { 0316 err = m_currentDocument->undoRedoTransaction(SKGDocument::UNDO); 0317 } 0318 QApplication::restoreOverrideCursor(); 0319 0320 // status bar 0321 IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Undo successfully done."))) 0322 else { 0323 err.addError(ERR_FAIL, i18nc("Error message", "Undo failed")); 0324 } 0325 0326 // Display error 0327 SKGMainPanel::displayErrorMessage(err); 0328 } 0329 } 0330 0331 void SKGUndoRedoPlugin::onShowUndoMenu() 0332 { 0333 if ((m_undoMenu != nullptr) && (m_currentDocument != nullptr)) { 0334 m_undoMenu->clear(); 0335 SKGStringListList listTmp; 0336 m_currentDocument->executeSelectSqliteOrder( 0337 QStringLiteral("SELECT t_name, t_savestep FROM doctransaction WHERE t_mode='U' ORDER BY d_date DESC LIMIT 7"), 0338 listTmp); 0339 int nb = listTmp.count(); 0340 for (int i = 1; i < nb; ++i) { 0341 QAction* act = m_undoMenu->addAction(listTmp.at(i).at(1) == QStringLiteral("Y") ? SKGServices::fromTheme(QStringLiteral("document-revert")) : SKGServices::fromTheme(QStringLiteral("edit-undo")), listTmp.at(i).at(0)); 0342 if (act != nullptr) { 0343 act->setData(i); 0344 connect(act, &QAction::triggered, this, &SKGUndoRedoPlugin::onUndo); 0345 } 0346 } 0347 } 0348 } 0349 0350 void SKGUndoRedoPlugin::onRedo() 0351 { 0352 SKGError err; 0353 SKGTRACEINFUNCRC(10, err) 0354 if ((m_currentDocument != nullptr) && (SKGMainPanel::getMainPanel() != nullptr)) { 0355 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 0356 int pos = qobject_cast<QAction*>(sender())->data().toInt(); 0357 for (int i = 1 ; !err && i <= pos; ++i) { 0358 err = m_currentDocument->undoRedoTransaction(SKGDocument::REDO); 0359 } 0360 QApplication::restoreOverrideCursor(); 0361 0362 // status bar 0363 IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Redo successfully done."))) 0364 else { 0365 err.addError(ERR_FAIL, i18nc("Error message", "Redo failed")); 0366 } 0367 0368 // Display error 0369 SKGMainPanel::displayErrorMessage(err); 0370 } 0371 } 0372 0373 void SKGUndoRedoPlugin::onShowRedoMenu() 0374 { 0375 if ((m_redoMenu != nullptr) && (m_currentDocument != nullptr)) { 0376 m_redoMenu->clear(); 0377 SKGStringListList listTmp; 0378 m_currentDocument->executeSelectSqliteOrder( 0379 QStringLiteral("SELECT t_name FROM doctransaction WHERE t_mode='R' ORDER BY d_date ASC LIMIT 7"), 0380 listTmp); 0381 int nb = listTmp.count(); 0382 for (int i = 1; i < nb; ++i) { 0383 QAction* act = m_redoMenu->addAction(SKGServices::fromTheme(QStringLiteral("edit-redo")), listTmp.at(i).at(0)); 0384 if (act != nullptr) { 0385 act->setData(i); 0386 connect(act, &QAction::triggered, this, &SKGUndoRedoPlugin::onRedo); 0387 } 0388 } 0389 } 0390 } 0391 0392 QDockWidget* SKGUndoRedoPlugin::getDockWidget() 0393 { 0394 return m_dockWidget; 0395 } 0396 0397 #include <skgundoredoplugin.moc>