File indexing completed on 2024-04-28 17:05:55

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 "packguibase.h"
0010 
0011 // QtCore
0012 #include <QVariant>
0013 // QtGui
0014 #include <QGuiApplication>
0015 #include <QImage>
0016 #include <QKeyEvent>
0017 #include <QPixmap>
0018 // QtWidgets
0019 #include <QCheckBox>
0020 #include <QComboBox>
0021 #include <QFrame>
0022 #include <QGridLayout>
0023 #include <QHBoxLayout>
0024 #include <QLabel>
0025 #include <QLayout>
0026 #include <QLineEdit>
0027 #include <QPushButton>
0028 #include <QSlider>
0029 #include <QSpinBox>
0030 #include <QToolButton>
0031 #include <QVBoxLayout>
0032 
0033 #include <KCompletion/KComboBox>
0034 #include <KConfigCore/KSharedConfig>
0035 #include <KI18n/KLocalizedString>
0036 #include <KIO/Global>
0037 #include <KWidgetsAddons/KMessageBox>
0038 #include <KWidgetsAddons/KStandardGuiItem>
0039 
0040 #include "../GUI/krhistorycombobox.h"
0041 #include "../compat.h"
0042 #include "../defaults.h"
0043 #include "../icon.h"
0044 #include "../krglobal.h"
0045 
0046 /*
0047  *  Constructs a PackGUIBase which is a child of 'parent', with the
0048  *  name 'name' and widget flags set to 'f'
0049  *
0050  *  The dialog will by default be modeless, unless you set 'modal' to
0051  *  TRUE to construct a modal dialog.
0052  */
0053 PackGUIBase::PackGUIBase(QWidget *parent)
0054     : QDialog(parent)
0055     , expanded(false)
0056 {
0057     KConfigGroup group(krConfig, "Archives");
0058 
0059     setModal(true);
0060     resize(430, 140);
0061     setWindowTitle(i18n("Pack"));
0062     grid = new QGridLayout(this);
0063     grid->setSpacing(6);
0064     grid->setContentsMargins(11, 11, 11, 11);
0065 
0066     hbox = new QHBoxLayout;
0067     hbox->setSpacing(6);
0068     hbox->setContentsMargins(0, 0, 0, 0);
0069 
0070     TextLabel3 = new QLabel(this);
0071     TextLabel3->setText(i18n("To archive"));
0072     hbox->addWidget(TextLabel3);
0073 
0074     nameData = new QLineEdit(this);
0075     hbox->addWidget(nameData);
0076 
0077     typeData = new QComboBox(this);
0078     typeData->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
0079     connect(typeData, QOverload<const QString &>::of(&QComboBox::QCOMBOBOX_ACTIVATED), this, &PackGUIBase::checkConsistency);
0080     connect(typeData, QOverload<const QString &>::of(&QComboBox::QCOMBOBOX_HIGHLIGHTED), this, &PackGUIBase::checkConsistency);
0081     hbox->addWidget(typeData);
0082 
0083     grid->addLayout(hbox, 1, 0);
0084 
0085     hbox_2 = new QHBoxLayout;
0086     hbox_2->setSpacing(6);
0087     hbox_2->setContentsMargins(0, 0, 0, 0);
0088 
0089     TextLabel5 = new QLabel(this);
0090     TextLabel5->setText(i18n("In folder"));
0091     hbox_2->addWidget(TextLabel5);
0092 
0093     dirData = new QLineEdit(this);
0094     hbox_2->addWidget(dirData);
0095 
0096     browseButton = new QToolButton(this);
0097     browseButton->setIcon(Icon("document-open"));
0098     hbox_2->addWidget(browseButton);
0099     auto *spacer = new QSpacerItem(48, 20, QSizePolicy::Fixed, QSizePolicy::Fixed);
0100     hbox_2->addItem(spacer);
0101 
0102     grid->addLayout(hbox_2, 2, 0);
0103 
0104     hbox_3 = new QHBoxLayout;
0105     hbox_3->setSpacing(6);
0106     hbox_3->setContentsMargins(0, 0, 0, 0);
0107 
0108     PixmapLabel1 = new QLabel(this);
0109     PixmapLabel1->setPixmap(Icon("package-x-generic").pixmap(32));
0110     PixmapLabel1->setScaledContents(true);
0111     PixmapLabel1->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
0112     hbox_3->addWidget(PixmapLabel1);
0113 
0114     TextLabel1 = new QLabel(this);
0115     TextLabel1->setText(i18n("Pack"));
0116     hbox_3->addWidget(TextLabel1);
0117 
0118     grid->addLayout(hbox_3, 0, 0);
0119 
0120     hbox_4 = new QHBoxLayout;
0121     hbox_4->setSpacing(6);
0122     hbox_4->setContentsMargins(0, 0, 0, 0);
0123 
0124     auto *spacer_3 = new QSpacerItem(20, 26, QSizePolicy::Fixed, QSizePolicy::Expanding);
0125     hbox_4->addItem(spacer_3);
0126     grid->addLayout(hbox_4, 3, 0);
0127 
0128     advancedWidget = new QWidget(this);
0129 
0130     hbox_5 = new QGridLayout(advancedWidget);
0131     hbox_5->setSpacing(6);
0132     hbox_5->setContentsMargins(0, 0, 0, 0);
0133 
0134     auto *compressLayout = new QVBoxLayout;
0135     compressLayout->setSpacing(6);
0136     compressLayout->setContentsMargins(0, 0, 0, 0);
0137 
0138     multipleVolume = new QCheckBox(i18n("Multiple volume archive"), advancedWidget);
0139     connect(multipleVolume, &QCheckBox::toggled, this, &PackGUIBase::checkConsistency);
0140     compressLayout->addWidget(multipleVolume, 0, Qt::Alignment());
0141 
0142     auto *volumeHbox = new QHBoxLayout;
0143 
0144     auto *spacer_5 = new QSpacerItem(20, 26, QSizePolicy::Fixed, QSizePolicy::Fixed);
0145     volumeHbox->addItem(spacer_5);
0146 
0147     TextLabel7 = new QLabel(i18n("Size:"), advancedWidget);
0148     volumeHbox->addWidget(TextLabel7);
0149 
0150     volumeSpinBox = new QSpinBox(advancedWidget);
0151     volumeSpinBox->setMinimum(1);
0152     volumeSpinBox->setMaximum(9999);
0153     volumeSpinBox->setValue(1440);
0154     volumeHbox->addWidget(volumeSpinBox);
0155 
0156     volumeUnitCombo = new QComboBox(advancedWidget);
0157     volumeUnitCombo->addItem("B");
0158     volumeUnitCombo->addItem("KB");
0159     volumeUnitCombo->addItem("MB");
0160     volumeUnitCombo->setCurrentIndex(1);
0161     volumeHbox->addWidget(volumeUnitCombo);
0162 
0163     compressLayout->addLayout(volumeHbox);
0164 
0165     int level = group.readEntry("Compression level", _defaultCompressionLevel);
0166     setCompressionLevel = new QCheckBox(i18n("Set compression level"), advancedWidget);
0167     if (level != _defaultCompressionLevel)
0168         setCompressionLevel->setChecked(true);
0169     connect(setCompressionLevel, &QCheckBox::toggled, this, &PackGUIBase::checkConsistency);
0170     compressLayout->addWidget(setCompressionLevel, 0, Qt::Alignment());
0171 
0172     auto *sliderHbox = new QHBoxLayout;
0173 
0174     auto *spacer_6 = new QSpacerItem(20, 26, QSizePolicy::Fixed, QSizePolicy::Fixed);
0175     sliderHbox->addItem(spacer_6);
0176 
0177     QWidget *sliderVBoxWidget = new QWidget(advancedWidget);
0178     auto *sliderVBox = new QVBoxLayout(sliderVBoxWidget);
0179 
0180     compressionSlider = new QSlider(Qt::Horizontal, sliderVBoxWidget);
0181     compressionSlider->setMinimum(1);
0182     compressionSlider->setMaximum(9);
0183     compressionSlider->setPageStep(1);
0184     compressionSlider->setValue(level);
0185     compressionSlider->setTickPosition(QSlider::TicksBelow);
0186     sliderVBox->addWidget(compressionSlider);
0187 
0188     QWidget *minmaxWidget = new QWidget(sliderVBoxWidget);
0189     sliderVBox->addWidget(minmaxWidget);
0190 
0191     auto *minmaxHbox = new QHBoxLayout(minmaxWidget);
0192 
0193     minLabel = new QLabel(i18n("MIN"), minmaxWidget);
0194     maxLabel = new QLabel(i18n("MAX"), minmaxWidget);
0195     maxLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0196 
0197     minmaxHbox->addWidget(minLabel);
0198     minmaxHbox->addWidget(maxLabel);
0199 
0200     sliderHbox->addWidget(sliderVBoxWidget);
0201 
0202     compressLayout->addLayout(sliderHbox);
0203 
0204     compressLayout->addStretch(0);
0205     hbox_5->addLayout(compressLayout, 0, 0);
0206 
0207     QFrame *vline = new QFrame(advancedWidget);
0208     vline->setFrameStyle(QFrame::VLine | QFrame::Sunken);
0209     vline->setMinimumWidth(20);
0210     hbox_5->addWidget(vline, 0, 1);
0211 
0212     auto *passwordGrid = new QGridLayout;
0213     passwordGrid->setSpacing(6);
0214     passwordGrid->setContentsMargins(0, 0, 0, 0);
0215 
0216     TextLabel4 = new QLabel(advancedWidget);
0217     TextLabel4->setText(i18n("Password"));
0218     passwordGrid->addWidget(TextLabel4, 0, 0);
0219 
0220     password = new QLineEdit(advancedWidget);
0221     password->setEchoMode(QLineEdit::Password);
0222     connect(password, &QLineEdit::textChanged, this, &PackGUIBase::checkConsistency);
0223 
0224     passwordGrid->addWidget(password, 0, 1);
0225 
0226     TextLabel6 = new QLabel(advancedWidget);
0227     TextLabel6->setText(i18n("Again"));
0228     passwordGrid->addWidget(TextLabel6, 1, 0);
0229 
0230     passwordAgain = new QLineEdit(advancedWidget);
0231     passwordAgain->setEchoMode(QLineEdit::Password);
0232     connect(passwordAgain, &QLineEdit::textChanged, this, &PackGUIBase::checkConsistency);
0233 
0234     passwordGrid->addWidget(passwordAgain, 1, 1);
0235 
0236     auto *consistencyHbox = new QHBoxLayout;
0237 
0238     auto *spacer_cons = new QSpacerItem(48, 20, QSizePolicy::Expanding, QSizePolicy::Fixed);
0239     consistencyHbox->addItem(spacer_cons);
0240 
0241     passwordConsistencyLabel = new QLabel(advancedWidget);
0242     consistencyHbox->addWidget(passwordConsistencyLabel);
0243     passwordGrid->addLayout(consistencyHbox, 2, 0, 1, 2);
0244 
0245     encryptHeaders = new QCheckBox(i18n("Encrypt headers"), advancedWidget);
0246     passwordGrid->addWidget(encryptHeaders, 3, 0, 1, 2);
0247 
0248     auto *spacer_psw = new QSpacerItem(20, 20, QSizePolicy::Fixed, QSizePolicy::Expanding);
0249     passwordGrid->addItem(spacer_psw, 4, 0);
0250 
0251     hbox_5->addLayout(passwordGrid, 0, 2);
0252 
0253     hbox_7 = new QHBoxLayout;
0254     hbox_7->setSpacing(6);
0255     hbox_7->setContentsMargins(0, 0, 0, 0);
0256 
0257     TextLabel8 = new QLabel(i18n("Command line switches:"), advancedWidget);
0258     TextLabel8->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0259     hbox_7->addWidget(TextLabel8);
0260 
0261     commandLineSwitches = new KrHistoryComboBox(advancedWidget);
0262     commandLineSwitches->setMaxCount(25); // remember 25 items
0263     commandLineSwitches->setDuplicatesEnabled(false);
0264     commandLineSwitches->setMinimumContentsLength(10);
0265 
0266     QStringList list = group.readEntry("Command Line Switches", QStringList());
0267     commandLineSwitches->setHistoryItems(list);
0268 
0269     hbox_7->addWidget(commandLineSwitches);
0270 
0271     hbox_5->addLayout(hbox_7, 1, 0, 1, 3);
0272 
0273     advancedWidget->hide();
0274     checkConsistency();
0275 
0276     grid->addWidget(advancedWidget, 4, 0);
0277 
0278     hbox_6 = new QHBoxLayout;
0279     hbox_6->setSpacing(6);
0280     hbox_6->setContentsMargins(0, 0, 0, 0);
0281 
0282     advancedButton = new QPushButton(this);
0283     advancedButton->setText(i18n("&Advanced >>"));
0284     hbox_6->addWidget(advancedButton);
0285 
0286     auto *spacer_2 = new QSpacerItem(140, 20, QSizePolicy::Expanding, QSizePolicy::Fixed);
0287     hbox_6->addItem(spacer_2);
0288 
0289     okButton = new QPushButton(this);
0290     KStandardGuiItem::assign(okButton, KStandardGuiItem::Ok);
0291     okButton->setDefault(true);
0292     hbox_6->addWidget(okButton);
0293 
0294     cancelButton = new QPushButton(this);
0295     KStandardGuiItem::assign(cancelButton, KStandardGuiItem::Cancel);
0296     hbox_6->addWidget(cancelButton);
0297 
0298     grid->addLayout(hbox_6, 6, 0);
0299 
0300     // signals and slots connections
0301     connect(okButton, &QPushButton::clicked, this, &PackGUIBase::accept);
0302     connect(advancedButton, &QPushButton::clicked, this, &PackGUIBase::expand);
0303     connect(cancelButton, &QPushButton::clicked, this, &PackGUIBase::reject);
0304     connect(browseButton, &QToolButton::clicked, this, &PackGUIBase::browse);
0305 }
0306 
0307 /*
0308  *  Destroys the object and frees any allocated resources
0309  */
0310 PackGUIBase::~PackGUIBase()
0311 {
0312     // no need to delete child widgets, Qt does it all for us
0313 }
0314 
0315 void PackGUIBase::browse()
0316 {
0317     qWarning("PackGUIBase::browse(): Not implemented yet!");
0318 }
0319 
0320 void PackGUIBase::expand()
0321 {
0322     expanded = !expanded;
0323 
0324     advancedButton->setText(expanded ? i18n("&Advanced <<") : i18n("&Advanced >>"));
0325 
0326     if (expanded)
0327         advancedWidget->show();
0328     else {
0329         advancedWidget->hide();
0330         layout()->activate();
0331         QSize minSize = minimumSize();
0332         resize(width(), minSize.height());
0333     }
0334     show();
0335 }
0336 
0337 void PackGUIBase::checkConsistency()
0338 {
0339     QPalette p = QGuiApplication::palette();
0340     QPalette pal = passwordConsistencyLabel->palette();
0341     if (password->text().isEmpty() && passwordAgain->text().isEmpty()) {
0342         pal.setColor(passwordConsistencyLabel->foregroundRole(), p.color(QPalette::Active, QPalette::Text));
0343         passwordConsistencyLabel->setText(i18n("No password specified"));
0344     } else if (password->text() == passwordAgain->text()) {
0345         pal.setColor(passwordConsistencyLabel->foregroundRole(), p.color(QPalette::Active, QPalette::Text));
0346         passwordConsistencyLabel->setText(i18n("The passwords are equal"));
0347     } else {
0348         pal.setColor(passwordConsistencyLabel->foregroundRole(), Qt::red);
0349         passwordConsistencyLabel->setText(i18n("The passwords are different"));
0350     }
0351     passwordConsistencyLabel->setPalette(pal);
0352 
0353     QString packer = typeData->currentText();
0354 
0355     bool passworded = false;
0356     if (packer == "7z" || packer == "rar" || packer == "zip" || packer == "arj")
0357         passworded = true;
0358 
0359     passwordConsistencyLabel->setEnabled(passworded);
0360     password->setEnabled(passworded);
0361     passwordAgain->setEnabled(passworded);
0362     TextLabel4->setEnabled(passworded);
0363     TextLabel6->setEnabled(passworded);
0364 
0365     encryptHeaders->setEnabled(packer == "rar");
0366 
0367     multipleVolume->setEnabled(packer == "rar" || packer == "arj");
0368     bool volumeEnabled = multipleVolume->isEnabled() && multipleVolume->isChecked();
0369     volumeSpinBox->setEnabled(volumeEnabled);
0370     volumeUnitCombo->setEnabled(volumeEnabled);
0371     TextLabel7->setEnabled(volumeEnabled);
0372 
0373     /* TODO */
0374     setCompressionLevel->setEnabled(packer == "rar" || packer == "arj" || packer == "zip" || packer == "7z");
0375     bool sliderEnabled = setCompressionLevel->isEnabled() && setCompressionLevel->isChecked();
0376     compressionSlider->setEnabled(sliderEnabled);
0377     minLabel->setEnabled(sliderEnabled);
0378     maxLabel->setEnabled(sliderEnabled);
0379 }
0380 
0381 bool PackGUIBase::extraProperties(QMap<QString, QString> &inMap)
0382 {
0383     inMap.clear();
0384 
0385     KConfigGroup group(krConfig, "Archives");
0386 
0387     if (password->isEnabled() && passwordAgain->isEnabled()) {
0388         if (password->text() != passwordAgain->text()) {
0389             KMessageBox::error(this, i18n("Cannot pack, the passwords are different."));
0390             return false;
0391         }
0392 
0393         if (!password->text().isEmpty()) {
0394             inMap["Password"] = password->text();
0395 
0396             if (encryptHeaders->isEnabled() && encryptHeaders->isChecked())
0397                 inMap["EncryptHeaders"] = '1';
0398         }
0399     }
0400 
0401     if (multipleVolume->isEnabled() && multipleVolume->isChecked()) {
0402         KIO::filesize_t size = volumeSpinBox->value();
0403 
0404         switch (volumeUnitCombo->currentIndex()) {
0405         case 2:
0406             size *= 1000;
0407 #if __GNUC__ >= 7
0408             [[gnu::fallthrough]];
0409 #endif
0410         case 1:
0411             size *= 1000;
0412         default:
0413             break;
0414         }
0415 
0416         if (size < 10000) {
0417             KMessageBox::error(this, i18n("Invalid volume size."));
0418             return false;
0419         }
0420 
0421         QString sbuffer;
0422         sbuffer.asprintf("%llu", size);
0423 
0424         inMap["VolumeSize"] = sbuffer;
0425     }
0426 
0427     if (setCompressionLevel->isEnabled() && setCompressionLevel->isChecked()) {
0428         inMap["CompressionLevel"] = QString("%1").arg(compressionSlider->value());
0429         int level = compressionSlider->value();
0430         group.writeEntry("Compression level", level);
0431     }
0432 
0433     QString cmdArgs = commandLineSwitches->currentText().trimmed();
0434     if (!cmdArgs.isEmpty()) {
0435         bool firstChar = true;
0436         QChar quote = QChar::Null;
0437 
0438         for (int i = 0; i < cmdArgs.length(); i++) {
0439             QChar ch(cmdArgs[i]);
0440             if (ch.isSpace())
0441                 continue;
0442 
0443             if (ch == quote) {
0444                 quote = QChar::Null;
0445                 continue;
0446             }
0447 
0448             if (firstChar && ch != QLatin1Char('-')) {
0449                 KMessageBox::error(this, i18n("Invalid command line switch.\nA switch must start with '-'."));
0450                 return false;
0451             }
0452 
0453             firstChar = false;
0454 
0455             if (quote == QLatin1Char('"'))
0456                 continue;
0457             if (quote == QChar::Null && (ch == QLatin1Char('\'') || ch == QLatin1Char('"')))
0458                 quote = ch;
0459             if (ch == QLatin1Char('\\')) {
0460                 if (i == cmdArgs.length() - 1) {
0461                     KMessageBox::error(this, i18n("Invalid command line switch.\nBackslashes cannot be the last character."));
0462                     return false;
0463                 }
0464                 i++;
0465             }
0466         }
0467 
0468         if (quote != QChar::Null) {
0469             KMessageBox::error(this, i18n("Invalid command line switch.\nUnclosed quotation mark."));
0470             return false;
0471         }
0472 
0473         commandLineSwitches->addToHistory(cmdArgs);
0474 
0475         inMap["CommandLineSwitches"] = cmdArgs;
0476     }
0477 
0478     return true;
0479 }