File indexing completed on 2024-04-28 17:06:04

0001 /*
0002     SPDX-FileCopyrightText: 2000 Shie Erlich <krusader@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2000 Rafi Yanai <krusader@users.sourceforge.net>
0004     SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "kcmdline.h"
0010 
0011 // QtCore
0012 #include <QDir>
0013 #include <QStringList>
0014 #include <QUrl>
0015 // QtGui
0016 #include <QFontDatabase>
0017 #include <QFontMetrics>
0018 #include <QIcon>
0019 #include <QImage>
0020 #include <QKeyEvent>
0021 // QtWidgets
0022 #include <QFrame>
0023 #include <QGridLayout>
0024 #include <QLabel>
0025 #include <QSizePolicy>
0026 
0027 #include <KConfigCore/KSharedConfig>
0028 #include <KI18n/KLocalizedString>
0029 #include <utility>
0030 
0031 #include "../ActionMan/addplaceholderpopup.h"
0032 #include "../compat.h"
0033 #include "../defaults.h"
0034 #include "../icon.h"
0035 #include "../krglobal.h"
0036 #include "../krservices.h"
0037 #include "../krslots.h"
0038 #include "../krusaderview.h"
0039 #include "kcmdmodebutton.h"
0040 
0041 CmdLineCombo::CmdLineCombo(QWidget *parent)
0042     : KrHistoryComboBox(parent)
0043     , _handlingLineEditResize(false)
0044 {
0045     lineEdit()->installEventFilter(this);
0046     _pathLabel = new QLabel(this);
0047     _pathLabel->setWhatsThis(i18n("Name of folder where command will be processed."));
0048     _pathLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
0049 }
0050 
0051 bool CmdLineCombo::eventFilter(QObject *watched, QEvent *e)
0052 {
0053     if (watched == lineEdit() && (e->type() == QEvent::Move || e->type() == QEvent::Resize)) {
0054         if (!_handlingLineEditResize) { // avoid infinite recursion
0055             _handlingLineEditResize = true;
0056             updateLineEditGeometry();
0057             _handlingLineEditResize = false;
0058         }
0059     }
0060     return false;
0061 }
0062 
0063 void CmdLineCombo::setPath(QString path)
0064 {
0065     _path = std::move(path);
0066     doLayout();
0067 }
0068 
0069 void CmdLineCombo::updateLineEditGeometry()
0070 {
0071     QRect r = lineEdit()->geometry();
0072     r.setLeft(_pathLabel->geometry().right());
0073     lineEdit()->setGeometry(r);
0074 }
0075 
0076 void CmdLineCombo::doLayout()
0077 {
0078     QString pathNameLabel = _path;
0079     QFontMetrics fm(_pathLabel->fontMetrics());
0080     int textWidth = fm.horizontalAdvance(_path);
0081     int maxWidth = (width() + _pathLabel->width()) * 2 / 5;
0082     int letters = _path.length() / 2;
0083 
0084     while (letters && textWidth > maxWidth) {
0085         pathNameLabel = _path.left(letters) + "..." + _path.right(letters);
0086         letters--;
0087         textWidth = fm.horizontalAdvance(pathNameLabel);
0088     }
0089 
0090     _pathLabel->setText(pathNameLabel + "> ");
0091     _pathLabel->adjustSize();
0092 
0093     QStyleOptionComboBox opt;
0094     initStyleOption(&opt);
0095     QRect labelRect = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this);
0096     labelRect.adjust(2, 0, 0, 0);
0097     labelRect.setWidth(_pathLabel->width());
0098     _pathLabel->setGeometry(labelRect);
0099 
0100     updateLineEditGeometry();
0101 }
0102 
0103 void CmdLineCombo::resizeEvent(QResizeEvent *e)
0104 {
0105     KrHistoryComboBox::resizeEvent(e);
0106     doLayout();
0107 }
0108 
0109 void CmdLineCombo::keyPressEvent(QKeyEvent *e)
0110 {
0111     switch (e->key()) {
0112     case Qt::Key_Enter:
0113     case Qt::Key_Return:
0114         if (e->modifiers() & Qt::ControlModifier) {
0115             SLOTS->insertFileName((e->modifiers() & Qt::ShiftModifier) != 0);
0116             break;
0117         }
0118         KrHistoryComboBox::keyPressEvent(e);
0119         break;
0120     case Qt::Key_Down:
0121         if (e->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) {
0122             MAIN_VIEW->focusTerminalEmulator();
0123             return;
0124         } else
0125             KrHistoryComboBox::keyPressEvent(e);
0126         break;
0127     case Qt::Key_Up:
0128         if (e->modifiers() == Qt::ControlModifier || e->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) {
0129             emit returnToPanel();
0130             return;
0131         } else
0132             KrHistoryComboBox::keyPressEvent(e);
0133         break;
0134     case Qt::Key_Escape:
0135         if (e->modifiers() == 0) {
0136             emit returnToPanel();
0137             return;
0138         } else
0139             KrHistoryComboBox::keyPressEvent(e);
0140         break;
0141     default:
0142         KrHistoryComboBox::keyPressEvent(e);
0143     }
0144 }
0145 
0146 KCMDLine::KCMDLine(QWidget *parent)
0147     : QWidget(parent)
0148 {
0149     auto *layout = new QGridLayout(this);
0150     layout->setSpacing(0);
0151     layout->setContentsMargins(0, 0, 0, 0);
0152 
0153     int height = QFontMetrics(QFontDatabase::systemFont(QFontDatabase::GeneralFont)).height();
0154     height = height + 5 * (height > 14) + 6;
0155 
0156     // and editable command line
0157     completion.setMode(KUrlCompletion::FileCompletion);
0158     cmdLine = new CmdLineCombo(this);
0159     cmdLine->setMaxCount(100); // remember 100 commands
0160     cmdLine->setMinimumContentsLength(10);
0161     cmdLine->setDuplicatesEnabled(false);
0162     cmdLine->setMaximumHeight(height);
0163     cmdLine->setCompletionObject(&completion);
0164     cmdLine->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
0165     // load the history
0166     KConfigGroup grpSvr(krConfig, "Private");
0167     QStringList list = grpSvr.readEntry("cmdline history", QStringList());
0168     cmdLine->setHistoryItems(list);
0169 
0170     connect(cmdLine, QOverload<const QString &>::of(&CmdLineCombo::returnPressed), this, &KCMDLine::slotRun);
0171     connect(cmdLine, QOverload<const QString &>::of(&CmdLineCombo::returnPressed), cmdLine->lineEdit(), &QLineEdit::clear);
0172     connect(cmdLine, &CmdLineCombo::returnToPanel, this, &KCMDLine::slotReturnFocus);
0173 
0174     cmdLine->setWhatsThis(
0175         i18n("<qt><p>Well, it is actually quite simple: you type your command here and Krusader obeys.</p><p><b>Tip</b>: move within command line history with "
0176              "&lt;Up&gt; and &lt;Down&gt; arrows.</p></qt>"));
0177     layout->addWidget(cmdLine, 0, 1);
0178 
0179     buttonAddPlaceholder = new QToolButton(this);
0180     buttonAddPlaceholder->setAutoRaise(true);
0181     buttonAddPlaceholder->setIcon(Icon("list-add"));
0182     connect(buttonAddPlaceholder, &QToolButton::clicked, this, &KCMDLine::addPlaceholder);
0183     buttonAddPlaceholder->setWhatsThis(i18n("Add <b>Placeholders</b> for the selected files in the panel."));
0184 
0185     layout->addWidget(buttonAddPlaceholder, 0, 2);
0186 
0187     // a run in terminal button
0188     terminal = new KCMDModeButton(this);
0189     terminal->setAutoRaise(true);
0190     layout->addWidget(terminal, 0, 3);
0191 
0192     layout->activate();
0193 }
0194 
0195 KCMDLine::~KCMDLine()
0196 {
0197     KConfigGroup grpSvr(krConfig, "Private");
0198     QStringList list = cmdLine->historyItems();
0199     // qWarning() << list[0];
0200     grpSvr.writeEntry("cmdline history", list);
0201     krConfig->sync();
0202 }
0203 
0204 void KCMDLine::addPlaceholder()
0205 {
0206     AddPlaceholderPopup popup(this);
0207     QString exp = popup.getPlaceholder(buttonAddPlaceholder->mapToGlobal(QPoint(0, 0)));
0208     this->addText(exp);
0209 }
0210 
0211 void KCMDLine::setCurrent(const QString &path)
0212 {
0213     cmdLine->setPath(path);
0214 
0215     completion.setDir(QUrl::fromLocalFile(path));
0216     // make sure our command is executed in the right directory
0217     // This line is important for Krusader overall functions -> do not remove !
0218     QDir::setCurrent(path);
0219 }
0220 
0221 void KCMDLine::slotRun()
0222 {
0223     const QString command1(cmdLine->currentText());
0224     if (command1.isEmpty())
0225         return;
0226 
0227     cmdLine->addToHistory(command1);
0228     // bugfix by aardvark: current editline is destroyed by addToHistory() in some cases
0229     cmdLine->setEditText(command1);
0230 
0231     if (command1.simplified().left(3) == "cd ") { // cd command effect the active panel
0232         QString dir = command1.right(command1.length() - command1.indexOf(" ")).trimmed();
0233         if (dir == "~")
0234             dir = QDir::homePath();
0235         else if (dir.left(1) != "/" && !dir.contains(":/"))
0236             dir = cmdLine->path() + (cmdLine->path() == "/" ? "" : "/") + dir;
0237         SLOTS->refresh(QUrl::fromUserInput(dir, QDir::currentPath(), QUrl::AssumeLocalFile));
0238     } else {
0239         exec();
0240         cmdLine->clearEditText();
0241     }
0242 }
0243 
0244 void KCMDLine::slotReturnFocus()
0245 {
0246     MAIN_VIEW->cmdLineUnFocus();
0247 }
0248 
0249 static const KrActionBase::ExecType execModesMenu[] = {
0250     KrActionBase::Normal,
0251     KrActionBase::CollectOutputSeparateStderr,
0252     KrActionBase::CollectOutput,
0253     KrActionBase::Terminal,
0254     KrActionBase::RunInTE,
0255 };
0256 
0257 QString KCMDLine::command() const
0258 {
0259     return cmdLine->currentText();
0260 }
0261 
0262 KrActionBase::ExecType KCMDLine::execType() const
0263 {
0264     KConfigGroup grp(krConfig, "Private");
0265     int i = grp.readEntry("Command Execution Mode", (int)0);
0266     return execModesMenu[i];
0267 }
0268 
0269 QString KCMDLine::startpath() const
0270 {
0271     return cmdLine->path();
0272     //     return path->text().left(path->text().length() - 1);
0273 }
0274 
0275 QString KCMDLine::user() const
0276 {
0277     return QString();
0278 }
0279 
0280 QString KCMDLine::text() const
0281 {
0282     return cmdLine->currentText();
0283 }
0284 
0285 bool KCMDLine::acceptURLs() const
0286 {
0287     return false;
0288 }
0289 
0290 bool KCMDLine::confirmExecution() const
0291 {
0292     return false;
0293 }
0294 
0295 bool KCMDLine::doSubstitution() const
0296 {
0297     return true;
0298 }
0299 
0300 void KCMDLine::setText(const QString &text)
0301 {
0302     cmdLine->lineEdit()->setText(text);
0303 }