File indexing completed on 2024-05-05 17:56:45
0001 /* 0002 SPDX-FileCopyrightText: 2006 Shie Erlich <erlich@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2006 Rafi Yanai <yanai@users.sourceforge.net> 0004 SPDX-FileCopyrightText: 2006-2022 Krusader Krew <https://krusader.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "useractionpage.h" 0010 0011 // QtWidgets 0012 #include <QFileDialog> 0013 #include <QHBoxLayout> 0014 #include <QLayout> 0015 #include <QSplitter> 0016 #include <QToolButton> 0017 #include <QVBoxLayout> 0018 // QtGui 0019 #include <QClipboard> 0020 // QtXml 0021 #include <QDomDocumentType> 0022 0023 #include <KCompletion/KLineEdit> 0024 #include <KI18n/KLocalizedString> 0025 #include <KWidgetsAddons/KMessageBox> 0026 #include <KWidgetsAddons/KStandardGuiItem> 0027 0028 #include "../UserAction/kraction.h" 0029 #include "../UserAction/useraction.h" 0030 #include "../icon.h" 0031 #include "../krglobal.h" 0032 #include "../krusader.h" 0033 #include "actionproperty.h" 0034 #include "useractionlistview.h" 0035 0036 // This is the filter in the QFileDialog of Import/Export: 0037 static const char *FILE_FILTER = I18N_NOOP("*.xml|XML files\n*|All files"); 0038 0039 UserActionPage::UserActionPage(QWidget *parent) 0040 : QWidget(parent) 0041 { 0042 auto *layout = new QVBoxLayout(this); 0043 layout->setContentsMargins(0, 0, 0, 0); 0044 layout->setSpacing(6); // 0px margin, 6px item-spacing 0045 0046 // ======== pseudo-toolbar start ======== 0047 auto *toolbarLayout = new QHBoxLayout; // neither margin nor spacing for the toolbar with autoRaise 0048 toolbarLayout->setSpacing(0); 0049 toolbarLayout->setContentsMargins(0, 0, 0, 0); 0050 0051 newButton = new QToolButton(this); 0052 newButton->setIcon(Icon("document-new")); 0053 newButton->setAutoRaise(true); 0054 newButton->setToolTip(i18n("Create new useraction")); 0055 0056 importButton = new QToolButton(this); 0057 importButton->setIcon(Icon("document-import")); 0058 importButton->setAutoRaise(true); 0059 importButton->setToolTip(i18n("Import useractions")); 0060 0061 exportButton = new QToolButton(this); 0062 exportButton->setIcon(Icon("document-export")); 0063 exportButton->setAutoRaise(true); 0064 exportButton->setToolTip(i18n("Export useractions")); 0065 0066 copyButton = new QToolButton(this); 0067 copyButton->setIcon(Icon("edit-copy")); 0068 copyButton->setAutoRaise(true); 0069 copyButton->setToolTip(i18n("Copy useractions to clipboard")); 0070 0071 pasteButton = new QToolButton(this); 0072 pasteButton->setIcon(Icon("edit-paste")); 0073 pasteButton->setAutoRaise(true); 0074 pasteButton->setToolTip(i18n("Paste useractions from clipboard")); 0075 0076 removeButton = new QToolButton(this); 0077 removeButton->setIcon(Icon("edit-delete")); 0078 removeButton->setAutoRaise(true); 0079 removeButton->setToolTip(i18n("Delete selected useractions")); 0080 0081 toolbarLayout->addWidget(newButton); 0082 toolbarLayout->addWidget(importButton); 0083 toolbarLayout->addWidget(exportButton); 0084 toolbarLayout->addWidget(copyButton); 0085 toolbarLayout->addWidget(pasteButton); 0086 toolbarLayout->addSpacing(6); // 6 pixel nothing 0087 toolbarLayout->addWidget(removeButton); 0088 toolbarLayout->addStretch(1000); // some very large stretch-factor 0089 // ======== pseudo-toolbar end ======== 0090 /* This seems obsolete now! 0091 // Display some help 0092 KMessageBox::information( this, // parent 0093 i18n( "When you apply changes to an action, the modifications " 0094 "become available in the current session immediately.\n" 0095 "When closing ActionMan, you will be asked to save the changes permanently." 0096 ), 0097 QString(), // caption 0098 "show UserAction help" //dontShowAgainName for the config 0099 ); 0100 */ 0101 layout->addLayout(toolbarLayout); 0102 auto *split = new QSplitter(this); 0103 layout->addWidget(split, 1000); // again a very large stretch-factor to fix the height of the toolbar 0104 0105 actionTree = new UserActionListView(split); 0106 actionTree->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 0107 actionProperties = new ActionProperty(split); 0108 actionProperties->setEnabled(false); // if there are any actions in the list, the first is displayed and this widget is enabled 0109 0110 connect(actionTree, &UserActionListView::currentItemChanged, this, &UserActionPage::slotChangeCurrent); 0111 connect(newButton, &QToolButton::clicked, this, &UserActionPage::slotNewAction); 0112 connect(removeButton, &QToolButton::clicked, this, &UserActionPage::slotRemoveAction); 0113 connect(importButton, &QToolButton::clicked, this, &UserActionPage::slotImport); 0114 connect(exportButton, &QToolButton::clicked, this, &UserActionPage::slotExport); 0115 connect(copyButton, &QToolButton::clicked, this, &UserActionPage::slotToClip); 0116 connect(pasteButton, &QToolButton::clicked, this, &UserActionPage::slotFromClip); 0117 0118 // forwards the changed signal of the properties 0119 connect(actionProperties, SIGNAL(changed()), SIGNAL(changed())); 0120 0121 actionTree->setFirstActionCurrent(); 0122 actionTree->setFocus(); 0123 } 0124 0125 UserActionPage::~UserActionPage() = default; 0126 0127 bool UserActionPage::continueInSpiteOfChanges() 0128 { 0129 if (!actionProperties->isModified()) 0130 return true; 0131 0132 int answer = KMessageBox::questionYesNoCancel(this, i18n("The current action has been modified. Do you want to apply these changes?")); 0133 if (answer == KMessageBox::Cancel) { 0134 disconnect(actionTree, &UserActionListView::currentItemChanged, this, &UserActionPage::slotChangeCurrent); 0135 actionTree->setCurrentAction(actionProperties->action()); 0136 connect(actionTree, &UserActionListView::currentItemChanged, this, &UserActionPage::slotChangeCurrent); 0137 return false; 0138 } 0139 if (answer == KMessageBox::Yes) { 0140 if (!actionProperties->validProperties()) { 0141 disconnect(actionTree, &UserActionListView::currentItemChanged, this, &UserActionPage::slotChangeCurrent); 0142 actionTree->setCurrentAction(actionProperties->action()); 0143 connect(actionTree, &UserActionListView::currentItemChanged, this, &UserActionPage::slotChangeCurrent); 0144 return false; 0145 } 0146 slotUpdateAction(); 0147 } // if Yes 0148 return true; 0149 } 0150 0151 void UserActionPage::slotChangeCurrent() 0152 { 0153 if (!continueInSpiteOfChanges()) 0154 return; 0155 0156 KrAction *action = actionTree->currentAction(); 0157 if (action) { 0158 actionProperties->setEnabled(true); 0159 // the distinct name is used as ID it is not allowed to change it afterwards because it is may referenced anywhere else 0160 actionProperties->leDistinctName->setEnabled(false); 0161 actionProperties->updateGUI(action); 0162 } else { 0163 // If the current item in the tree is no action (i.e. a category), disable the properties 0164 actionProperties->clear(); 0165 actionProperties->setEnabled(false); 0166 } 0167 emit applied(); // to disable the apply-button 0168 } 0169 0170 void UserActionPage::slotUpdateAction() 0171 { 0172 // check that we have a command line, title and a name 0173 if (!actionProperties->validProperties()) 0174 return; 0175 0176 if (actionProperties->leDistinctName->isEnabled()) { 0177 // := new entry 0178 KrAction *action = new KrAction(krApp->actionCollection(), actionProperties->leDistinctName->text()); 0179 krUserAction->addKrAction(action); 0180 actionProperties->updateAction(action); 0181 UserActionListViewItem *item = actionTree->insertAction(action); 0182 actionTree->setCurrentItem(item); 0183 } else { // := edit an existing 0184 actionProperties->updateAction(); 0185 actionTree->update(actionProperties->action()); // update the listviewitem as well... 0186 } 0187 apply(); 0188 } 0189 0190 void UserActionPage::slotNewAction() 0191 { 0192 if (continueInSpiteOfChanges()) { 0193 actionTree->clearSelection(); // else the user may think that he is overwriting the selected action 0194 actionProperties->clear(); 0195 actionProperties->setEnabled(true); // it may be disabled because the tree has the focus on a category 0196 actionProperties->leDistinctName->setEnabled(true); 0197 actionProperties->leDistinctName->setFocus(); 0198 } 0199 } 0200 0201 void UserActionPage::slotRemoveAction() 0202 { 0203 if (!dynamic_cast<UserActionListViewItem *>(actionTree->currentItem())) 0204 return; 0205 0206 int messageDelete = KMessageBox::warningContinueCancel(this, // parent 0207 i18n("Are you sure that you want to remove all selected actions?"), // text 0208 i18n("Remove Selected Actions?"), // caption 0209 KStandardGuiItem::remove(), // Label for the continue-button 0210 KStandardGuiItem::cancel(), 0211 "Confirm Remove UserAction", // dontAskAgainName (for the config-file) 0212 KMessageBox::Dangerous | KMessageBox::Notify); 0213 0214 if (messageDelete != KMessageBox::Continue) 0215 return; 0216 0217 actionProperties->clear(); 0218 actionProperties->setEnabled(false); 0219 0220 actionTree->removeSelectedActions(); 0221 0222 apply(); 0223 } 0224 0225 void UserActionPage::slotImport() 0226 { 0227 QString filename = QFileDialog::getOpenFileName(this, QString(), QString(), i18n(FILE_FILTER)); 0228 if (filename.isEmpty()) 0229 return; 0230 0231 UserAction::KrActionList newActions; 0232 krUserAction->readFromFile(filename, UserAction::renameDoublicated, &newActions); 0233 0234 QListIterator<KrAction *> it(newActions); 0235 while (it.hasNext()) 0236 actionTree->insertAction(it.next()); 0237 0238 if (newActions.count() > 0) { 0239 apply(); 0240 } 0241 } 0242 0243 void UserActionPage::slotExport() 0244 { 0245 if (!dynamic_cast<UserActionListViewItem *>(actionTree->currentItem())) 0246 return; 0247 0248 QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), i18n(FILE_FILTER)); 0249 if (filename.isEmpty()) 0250 return; 0251 0252 QDomDocument doc = QDomDocument(ACTION_DOCTYPE); 0253 QFile file(filename); 0254 int answer = 0; 0255 if (file.open(QIODevice::ReadOnly)) { // getting here, means the file already exists an can be read 0256 if (doc.setContent(&file)) // getting here means the file exists and already contains an UserAction-XML-tree 0257 answer = KMessageBox::warningYesNoCancel( 0258 this, // parent 0259 i18n("This file already contains some useractions.\nDo you want to overwrite it or should it be merged with the selected actions?"), // text 0260 i18n("Overwrite or Merge?"), // caption 0261 KStandardGuiItem::overwrite(), // label for Yes-Button 0262 KGuiItem(i18n("Merge")) // label for No-Button 0263 ); 0264 file.close(); 0265 } 0266 if (answer == 0 && file.exists()) 0267 answer = KMessageBox::warningContinueCancel(this, // parent 0268 i18n("This file already exists. Do you want to overwrite it?"), // text 0269 i18n("Overwrite Existing File?"), // caption 0270 KStandardGuiItem::overwrite() // label for Continue-Button 0271 ); 0272 0273 if (answer == KMessageBox::Cancel) 0274 return; 0275 0276 if (answer == KMessageBox::No) // that means the merge-button 0277 doc = actionTree->dumpSelectedActions(&doc); // merge 0278 else // Yes or Continue means overwrite 0279 doc = actionTree->dumpSelectedActions(); 0280 0281 bool success = UserAction::writeToFile(doc, filename); 0282 if (!success) 0283 KMessageBox::error(this, i18n("Cannot open %1 for writing.\nNothing exported.", filename), i18n("Export Failed")); 0284 } 0285 0286 void UserActionPage::slotToClip() 0287 { 0288 if (!dynamic_cast<UserActionListViewItem *>(actionTree->currentItem())) 0289 return; 0290 0291 QDomDocument doc = actionTree->dumpSelectedActions(); 0292 QApplication::clipboard()->setText(doc.toString()); 0293 } 0294 0295 void UserActionPage::slotFromClip() 0296 { 0297 QDomDocument doc(ACTION_DOCTYPE); 0298 if (doc.setContent(QApplication::clipboard()->text())) { 0299 QDomElement root = doc.documentElement(); 0300 UserAction::KrActionList newActions; 0301 krUserAction->readFromElement(root, UserAction::renameDoublicated, &newActions); 0302 0303 QListIterator<KrAction *> it(newActions); 0304 while (it.hasNext()) 0305 actionTree->insertAction(it.next()); 0306 0307 if (newActions.count() > 0) { 0308 apply(); 0309 } 0310 } // if ( doc.setContent ) 0311 } 0312 0313 bool UserActionPage::readyToQuit() 0314 { 0315 // Check if the current UserAction has changed 0316 if (!continueInSpiteOfChanges()) 0317 return false; 0318 0319 krUserAction->writeActionFile(); 0320 return true; 0321 } 0322 0323 void UserActionPage::apply() 0324 { 0325 krUserAction->writeActionFile(); 0326 emit applied(); 0327 } 0328 0329 void UserActionPage::applyChanges() 0330 { 0331 slotUpdateAction(); 0332 }