File indexing completed on 2024-05-05 17:56:44

0001 /*
0002     SPDX-FileCopyrightText: 2004-2007 Jonas Bähr <jonas.baehr@web.de>
0003     SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "actionproperty.h"
0009 #include "addplaceholderpopup.h"
0010 
0011 #include "../UserAction/kraction.h"
0012 #include "../UserAction/useraction.h"
0013 #include "../icon.h"
0014 #include "../krglobal.h"
0015 #include "../krusader.h"
0016 
0017 // QtWidgets
0018 #include <QFileDialog>
0019 #include <QInputDialog>
0020 
0021 #include <KI18n/KLocalizedString>
0022 #include <KWidgetsAddons/KMessageBox>
0023 #include <KXmlGui/KActionCollection>
0024 
0025 ActionProperty::ActionProperty(QWidget *parent, KrAction *action)
0026     : QWidget(parent)
0027     , _modified(false)
0028 {
0029     setupUi(this);
0030 
0031     if (action) {
0032         _action = action;
0033         updateGUI(_action);
0034     }
0035 
0036     ButtonAddPlaceholder->setIcon(Icon("list-add"));
0037     ButtonAddStartpath->setIcon(Icon("document-open"));
0038 
0039     // fill with all existing categories
0040     cbCategory->addItems(krUserAction->allCategories());
0041 
0042     connect(ButtonAddPlaceholder, &QToolButton::clicked, this, &ActionProperty::addPlaceholder);
0043     connect(ButtonAddStartpath, &QToolButton::clicked, this, &ActionProperty::addStartpath);
0044     connect(ButtonNewProtocol, &QToolButton::clicked, this, &ActionProperty::newProtocol);
0045     connect(ButtonEditProtocol, &QToolButton::clicked, this, &ActionProperty::editProtocol);
0046     connect(ButtonRemoveProtocol, &QToolButton::clicked, this, &ActionProperty::removeProtocol);
0047     connect(ButtonAddPath, &QToolButton::clicked, this, &ActionProperty::addPath);
0048     connect(ButtonEditPath, &QToolButton::clicked, this, &ActionProperty::editPath);
0049     connect(ButtonRemovePath, &QToolButton::clicked, this, &ActionProperty::removePath);
0050     connect(ButtonAddMime, &QToolButton::clicked, this, &ActionProperty::addMime);
0051     connect(ButtonEditMime, &QToolButton::clicked, this, &ActionProperty::editMime);
0052     connect(ButtonRemoveMime, &QToolButton::clicked, this, &ActionProperty::removeMime);
0053     connect(ButtonNewFile, &QToolButton::clicked, this, &ActionProperty::newFile);
0054     connect(ButtonEditFile, &QToolButton::clicked, this, &ActionProperty::editFile);
0055     connect(ButtonRemoveFile, &QToolButton::clicked, this, &ActionProperty::removeFile);
0056     connect(KeyButtonShortcut, &KKeySequenceWidget::keySequenceChanged, this, &ActionProperty::changedShortcut);
0057     // track modifications:
0058     connect(leDistinctName, &KLineEdit::textChanged, this, [=]() {
0059         setModified(true);
0060     });
0061     connect(leTitle, &KLineEdit::textChanged, this, [=]() {
0062         setModified(true);
0063     });
0064     connect(ButtonIcon, &KIconButton::iconChanged, this, [=]() {
0065         setModified(true);
0066     });
0067     connect(cbCategory, &KComboBox::currentTextChanged, this, [=]() {
0068         setModified(true);
0069     });
0070     connect(leTooltip, &KLineEdit::textChanged, this, [=]() {
0071         setModified(true);
0072     });
0073     connect(textDescription, &KTextEdit::textChanged, this, [=]() {
0074         setModified(true);
0075     });
0076     connect(leCommandline, &KLineEdit::textChanged, this, [=]() {
0077         setModified(true);
0078     });
0079     connect(leStartpath, &KLineEdit::textChanged, this, [=]() {
0080         setModified(true);
0081     });
0082     connect(chkSeparateStdError, &QCheckBox::clicked, this, &ActionProperty::setModified);
0083     connect(radioCollectOutput, &QRadioButton::clicked, this, &ActionProperty::setModified);
0084     connect(radioNormal, &QRadioButton::clicked, this, &ActionProperty::setModified);
0085     connect(radioTE, &QRadioButton::clicked, this, &ActionProperty::setModified);
0086     connect(radioTerminal, &QRadioButton::clicked, this, &ActionProperty::setModified);
0087     connect(radioLocal, &QRadioButton::clicked, this, &ActionProperty::setModified);
0088     connect(radioUrl, &QRadioButton::clicked, this, &ActionProperty::setModified);
0089     connect(KeyButtonShortcut, &KKeySequenceWidget::keySequenceChanged, this, [=]() {
0090         setModified(true);
0091     });
0092     connect(chkEnabled, &QCheckBox::clicked, this, &ActionProperty::setModified);
0093     connect(leDifferentUser, &KLineEdit::textChanged, this, [=]() {
0094         setModified(true);
0095     });
0096     connect(chkDifferentUser, &QCheckBox::clicked, this, &ActionProperty::setModified);
0097     connect(chkConfirmExecution, &QCheckBox::clicked, this, &ActionProperty::setModified);
0098     connect(chkSeparateStdError, &QCheckBox::clicked, this, &ActionProperty::setModified);
0099     // The modified-state of the ShowOnly-lists is tracked in the access-functions below
0100 }
0101 
0102 ActionProperty::~ActionProperty() = default;
0103 
0104 void ActionProperty::changedShortcut(const QKeySequence &shortcut)
0105 {
0106     KeyButtonShortcut->setKeySequence(shortcut);
0107 }
0108 
0109 void ActionProperty::clear()
0110 {
0111     _action = nullptr;
0112 
0113     // This prevents the changed-signal from being emitted during the GUI-update
0114     _modified = true; // The real state is set at the end of this function.
0115 
0116     leDistinctName->clear();
0117     cbCategory->clearEditText();
0118     leTitle->clear();
0119     leTooltip->clear();
0120     textDescription->clear();
0121     leCommandline->clear();
0122     leStartpath->clear();
0123     KeyButtonShortcut->clearKeySequence();
0124 
0125     lbShowonlyProtocol->clear();
0126     lbShowonlyPath->clear();
0127     lbShowonlyMime->clear();
0128     lbShowonlyFile->clear();
0129 
0130     chkSeparateStdError->setChecked(false);
0131     radioNormal->setChecked(true);
0132 
0133     radioLocal->setChecked(true);
0134 
0135     chkEnabled->setChecked(true);
0136 
0137     chkConfirmExecution->setChecked(false);
0138 
0139     ButtonIcon->resetIcon();
0140 
0141     leDifferentUser->clear();
0142     chkDifferentUser->setChecked(false);
0143 
0144     setModified(false);
0145 }
0146 
0147 void ActionProperty::updateGUI(KrAction *action)
0148 {
0149     if (action)
0150         _action = action;
0151     if (!_action)
0152         return;
0153 
0154     // This prevents the changed-signal from being emitted during the GUI-update.
0155     _modified = true; // The real state is set at the end of this function.
0156 
0157     leDistinctName->setText(_action->objectName());
0158     cbCategory->lineEdit()->setText(_action->category());
0159     leTitle->setText(_action->text());
0160     leTooltip->setText(_action->toolTip());
0161     textDescription->setText(_action->whatsThis());
0162     leCommandline->setText(_action->command());
0163     leCommandline->home(false);
0164     leStartpath->setText(_action->startpath());
0165     KeyButtonShortcut->setKeySequence(_action->shortcut());
0166 
0167     lbShowonlyProtocol->clear();
0168     lbShowonlyProtocol->addItems(_action->showonlyProtocol());
0169     lbShowonlyPath->clear();
0170     lbShowonlyPath->addItems(_action->showonlyPath());
0171     lbShowonlyMime->clear();
0172     lbShowonlyMime->addItems(_action->showonlyMime());
0173     lbShowonlyFile->clear();
0174     lbShowonlyFile->addItems(_action->showonlyFile());
0175 
0176     chkSeparateStdError->setChecked(false);
0177     switch (_action->execType()) {
0178     case KrAction::CollectOutputSeparateStderr:
0179         chkSeparateStdError->setChecked(true);
0180         radioCollectOutput->setChecked(true);
0181         break;
0182     case KrAction::CollectOutput:
0183         radioCollectOutput->setChecked(true);
0184         break;
0185     case KrAction::Terminal:
0186         radioTerminal->setChecked(true);
0187         break;
0188     case KrAction::RunInTE:
0189         radioTE->setChecked(true);
0190         break;
0191     default: // case KrAction::Normal:
0192         radioNormal->setChecked(true);
0193         break;
0194     }
0195 
0196     if (_action->acceptURLs())
0197         radioUrl->setChecked(true);
0198     else
0199         radioLocal->setChecked(true);
0200 
0201     chkEnabled->setChecked(_action->isVisible());
0202 
0203     chkConfirmExecution->setChecked(_action->confirmExecution());
0204 
0205     if (!_action->icon().isNull())
0206         ButtonIcon->setIcon(_action->iconName());
0207     else
0208         ButtonIcon->resetIcon();
0209 
0210     leDifferentUser->setText(_action->user());
0211     if (_action->user().isEmpty())
0212         chkDifferentUser->setChecked(false);
0213     else
0214         chkDifferentUser->setChecked(true);
0215 
0216     setModified(false);
0217 }
0218 
0219 void ActionProperty::updateAction(KrAction *action)
0220 {
0221     if (action)
0222         _action = action;
0223     if (!_action)
0224         return;
0225 
0226     if (_action->category() != cbCategory->currentText()) {
0227         _action->setCategory(cbCategory->currentText());
0228         // Update the category-list
0229         cbCategory->clear();
0230         cbCategory->addItems(krUserAction->allCategories());
0231         cbCategory->lineEdit()->setText(_action->category());
0232     }
0233 
0234     _action->setObjectName(leDistinctName->text());
0235     _action->setText(leTitle->text());
0236     _action->setToolTip(leTooltip->text());
0237     _action->setWhatsThis(textDescription->toPlainText());
0238     _action->setCommand(leCommandline->text());
0239     _action->setStartpath(leStartpath->text());
0240     _action->setShortcut(KeyButtonShortcut->keySequence());
0241 
0242     QStringList list;
0243 
0244     for (int i1 = 0; i1 != lbShowonlyProtocol->count(); i1++) {
0245         QListWidgetItem *lbi = lbShowonlyProtocol->item(i1);
0246 
0247         list << lbi->text();
0248     }
0249     _action->setShowonlyProtocol(list);
0250 
0251     list = QStringList();
0252     for (int i1 = 0; i1 != lbShowonlyPath->count(); i1++) {
0253         QListWidgetItem *lbi = lbShowonlyPath->item(i1);
0254 
0255         list << lbi->text();
0256     }
0257     _action->setShowonlyPath(list);
0258 
0259     list = QStringList();
0260     for (int i1 = 0; i1 != lbShowonlyMime->count(); i1++) {
0261         QListWidgetItem *lbi = lbShowonlyMime->item(i1);
0262 
0263         list << lbi->text();
0264     }
0265     _action->setShowonlyMime(list);
0266 
0267     list = QStringList();
0268     for (int i1 = 0; i1 != lbShowonlyFile->count(); i1++) {
0269         QListWidgetItem *lbi = lbShowonlyFile->item(i1);
0270 
0271         list << lbi->text();
0272     }
0273     _action->setShowonlyFile(list);
0274 
0275     if (radioCollectOutput->isChecked() && chkSeparateStdError->isChecked())
0276         _action->setExecType(KrAction::CollectOutputSeparateStderr);
0277     else if (radioCollectOutput->isChecked() && !chkSeparateStdError->isChecked())
0278         _action->setExecType(KrAction::CollectOutput);
0279     else if (radioTerminal->isChecked())
0280         _action->setExecType(KrAction::Terminal);
0281     else if (radioTE->isChecked())
0282         _action->setExecType(KrAction::RunInTE);
0283     else
0284         _action->setExecType(KrAction::Normal);
0285 
0286     if (radioUrl->isChecked())
0287         _action->setAcceptURLs(true);
0288     else
0289         _action->setAcceptURLs(false);
0290 
0291     _action->setEnabled(chkEnabled->isChecked());
0292     _action->setVisible(chkEnabled->isChecked());
0293 
0294     _action->setConfirmExecution(chkConfirmExecution->isChecked());
0295 
0296     _action->setIcon(Icon(ButtonIcon->icon()));
0297     _action->setIconName(ButtonIcon->icon());
0298 
0299     _action->setUser(leDifferentUser->text());
0300 
0301     setModified(false);
0302 }
0303 
0304 void ActionProperty::addPlaceholder()
0305 {
0306     AddPlaceholderPopup popup(this);
0307     QString exp = popup.getPlaceholder(mapToGlobal(QPoint(ButtonAddPlaceholder->pos().x() + ButtonAddPlaceholder->width() + 6, // 6 is the default margin
0308                                                           ButtonAddPlaceholder->pos().y())));
0309     leCommandline->insert(exp);
0310 }
0311 
0312 void ActionProperty::addStartpath()
0313 {
0314     QString folder = QFileDialog::getExistingDirectory(this);
0315     if (!folder.isEmpty()) {
0316         leStartpath->setText(folder);
0317     }
0318 }
0319 
0320 void ActionProperty::newProtocol()
0321 {
0322     bool ok;
0323 
0324     QString currentText;
0325     if (lbShowonlyProtocol->currentItem())
0326         currentText = lbShowonlyProtocol->currentItem()->text();
0327 
0328     QString text = QInputDialog::getText(this, i18n("New protocol"), i18n("Set a protocol:"), QLineEdit::Normal, currentText, &ok);
0329     if (ok && !text.isEmpty()) {
0330         lbShowonlyProtocol->addItems(text.split(';'));
0331         setModified();
0332     }
0333 }
0334 
0335 void ActionProperty::editProtocol()
0336 {
0337     if (lbShowonlyProtocol->currentItem() == nullptr)
0338         return;
0339 
0340     bool ok;
0341 
0342     QString currentText = lbShowonlyProtocol->currentItem()->text();
0343 
0344     QString text = QInputDialog::getText(this, i18n("Edit Protocol"), i18n("Set another protocol:"), QLineEdit::Normal, currentText, &ok);
0345     if (ok && !text.isEmpty()) {
0346         lbShowonlyProtocol->currentItem()->setText(text);
0347         setModified();
0348     }
0349 }
0350 
0351 void ActionProperty::removeProtocol()
0352 {
0353     if (lbShowonlyProtocol->currentItem() != nullptr) {
0354         delete lbShowonlyProtocol->currentItem();
0355         setModified();
0356     }
0357 }
0358 
0359 void ActionProperty::addPath()
0360 {
0361     QString folder = QFileDialog::getExistingDirectory(this);
0362     if (!folder.isEmpty()) {
0363         lbShowonlyPath->addItem(folder);
0364         setModified();
0365     }
0366 }
0367 
0368 void ActionProperty::editPath()
0369 {
0370     if (lbShowonlyPath->currentItem() == nullptr)
0371         return;
0372 
0373     bool ok;
0374 
0375     QString currentText = lbShowonlyPath->currentItem()->text();
0376 
0377     QString text = QInputDialog::getText(this, i18n("Edit Path"), i18n("Set another path:"), QLineEdit::Normal, currentText, &ok);
0378     if (ok && !text.isEmpty()) {
0379         lbShowonlyPath->currentItem()->setText(text);
0380         setModified();
0381     }
0382 }
0383 
0384 void ActionProperty::removePath()
0385 {
0386     if (lbShowonlyPath->currentItem() != nullptr) {
0387         delete lbShowonlyPath->currentItem();
0388         setModified();
0389     }
0390 }
0391 
0392 void ActionProperty::addMime()
0393 {
0394     bool ok;
0395 
0396     QString currentText;
0397     if (lbShowonlyMime->currentItem())
0398         currentText = lbShowonlyMime->currentItem()->text();
0399 
0400     QString text = QInputDialog::getText(this, i18n("New MIME Type"), i18n("Set a MIME type:"), QLineEdit::Normal, currentText, &ok);
0401     if (ok && !text.isEmpty()) {
0402         lbShowonlyMime->addItems(text.split(';'));
0403         setModified();
0404     }
0405 }
0406 
0407 void ActionProperty::editMime()
0408 {
0409     if (lbShowonlyMime->currentItem() == nullptr)
0410         return;
0411 
0412     bool ok;
0413 
0414     QString currentText = lbShowonlyMime->currentItem()->text();
0415 
0416     QString text = QInputDialog::getText(this, i18n("Edit MIME Type"), i18n("Set another MIME type:"), QLineEdit::Normal, currentText, &ok);
0417     if (ok && !text.isEmpty()) {
0418         lbShowonlyMime->currentItem()->setText(text);
0419         setModified();
0420     }
0421 }
0422 
0423 void ActionProperty::removeMime()
0424 {
0425     if (lbShowonlyMime->currentItem() != nullptr) {
0426         delete lbShowonlyMime->currentItem();
0427         setModified();
0428     }
0429 }
0430 
0431 void ActionProperty::newFile()
0432 {
0433     bool ok;
0434 
0435     QString currentText;
0436     if (lbShowonlyFile->currentItem())
0437         currentText = lbShowonlyFile->currentItem()->text();
0438 
0439     QString text = QInputDialog::getText(this, i18n("New File Name"), i18n("Set a file name:"), QLineEdit::Normal, currentText, &ok);
0440     if (ok && !text.isEmpty()) {
0441         lbShowonlyFile->addItems(text.split(';'));
0442         setModified();
0443     }
0444 }
0445 
0446 void ActionProperty::editFile()
0447 {
0448     if (lbShowonlyFile->currentItem() == nullptr)
0449         return;
0450 
0451     bool ok;
0452 
0453     QString currentText = lbShowonlyFile->currentItem()->text();
0454 
0455     QString text = QInputDialog::getText(this, i18n("Edit File Name"), i18n("Set another file name:"), QLineEdit::Normal, currentText, &ok);
0456     if (ok && !text.isEmpty()) {
0457         lbShowonlyFile->currentItem()->setText(text);
0458         setModified();
0459     }
0460 }
0461 
0462 void ActionProperty::removeFile()
0463 {
0464     if (lbShowonlyFile->currentItem() != nullptr) {
0465         delete lbShowonlyFile->currentItem();
0466         setModified();
0467     }
0468 }
0469 
0470 bool ActionProperty::validProperties()
0471 {
0472     if (leDistinctName->text().simplified().isEmpty()) {
0473         KMessageBox::error(this, i18n("Please set a unique name for the useraction"));
0474         leDistinctName->setFocus();
0475         return false;
0476     }
0477     if (leTitle->text().simplified().isEmpty()) {
0478         KMessageBox::error(this, i18n("Please set a title for the menu entry"));
0479         leTitle->setFocus();
0480         return false;
0481     }
0482     if (leCommandline->text().simplified().isEmpty()) {
0483         KMessageBox::error(this, i18n("Command line is empty"));
0484         leCommandline->setFocus();
0485         return false;
0486     }
0487     if (leDistinctName->isEnabled())
0488         if (krApp->actionCollection()->action(leDistinctName->text())) {
0489             KMessageBox::error(this,
0490                                i18n("There already is an action with this name.\n"
0491                                     "If you do not have such a useraction the name is used by Krusader for an internal action."));
0492             leDistinctName->setFocus();
0493             return false;
0494         }
0495 
0496     return true;
0497 }
0498 
0499 void ActionProperty::setModified(bool m)
0500 {
0501     if (m && !_modified) { // emit only when the state _changes_to_true_,
0502         emit changed();
0503     }
0504     _modified = m;
0505 }