File indexing completed on 2024-05-05 05:50:41

0001 /*
0002     SPDX-FileCopyrightText: 2009 Harald Hvaal <haraldhv@stud.ntnu.no>
0003 
0004     SPDX-License-Identifier: BSD-2-Clause
0005 */
0006 
0007 #include "extractiondialog.h"
0008 #include "ark_debug.h"
0009 #include "settings.h"
0010 
0011 #include <KDirOperator>
0012 #include <KLocalizedString>
0013 #include <KMessageBox>
0014 #include <KUrlComboBox>
0015 #include <KWindowConfig>
0016 
0017 #include <QDir>
0018 #include <QPushButton>
0019 
0020 #include "ui_extractiondialog.h"
0021 
0022 namespace Kerfuffle
0023 {
0024 class ExtractionDialogUI : public QFrame, public Ui::ExtractionDialog
0025 {
0026     Q_OBJECT
0027 
0028 public:
0029     ExtractionDialogUI(QWidget *parent = nullptr)
0030         : QFrame(parent)
0031     {
0032         setupUi(this);
0033     }
0034 };
0035 
0036 ExtractionDialog::ExtractionDialog(QWidget *parent)
0037     : QDialog(parent, Qt::Dialog)
0038 
0039 {
0040     setWindowTitle(i18nc("@title:window", "Extract"));
0041 
0042     QHBoxLayout *hlayout = new QHBoxLayout();
0043     hlayout->setContentsMargins({});
0044     hlayout->setSpacing(0);
0045     setLayout(hlayout);
0046 
0047     fileWidget = new KFileWidget(QUrl::fromLocalFile(QDir::homePath()), this);
0048     hlayout->addWidget(fileWidget);
0049 
0050     fileWidget->setMode(KFile::Directory | KFile::LocalOnly | KFile::ExistingOnly);
0051     fileWidget->setOperationMode(KFileWidget::Saving);
0052 
0053     // This signal is emitted e.g. when the user presses Return while in the location bar.
0054     connect(fileWidget, &KFileWidget::accepted, this, &ExtractionDialog::slotAccepted);
0055 
0056     fileWidget->okButton()->setText(i18n("Extract"));
0057     fileWidget->okButton()->show();
0058     connect(fileWidget->okButton(), &QPushButton::clicked, this, &ExtractionDialog::slotAccepted);
0059 
0060     fileWidget->cancelButton()->show();
0061     connect(fileWidget->cancelButton(), &QPushButton::clicked, this, &QDialog::reject);
0062 
0063     auto horizontalSeparator = new QFrame(this);
0064     horizontalSeparator->setFrameStyle(QFrame::VLine);
0065     horizontalSeparator->setMaximumWidth(1);
0066     hlayout->addWidget(horizontalSeparator);
0067 
0068     m_ui = new ExtractionDialogUI(this);
0069     hlayout->addWidget(m_ui);
0070 
0071     m_ui->iconLabel->setPixmap(QIcon::fromTheme(QStringLiteral("archive-extract")).pixmap(48));
0072 
0073     m_ui->filesToExtractGroupBox->hide();
0074     m_ui->allFilesButton->setChecked(true);
0075     m_ui->extractAllLabel->show();
0076 
0077     m_ui->autoSubfolders->hide();
0078 
0079     loadSettings();
0080 
0081     connect(this, &QDialog::finished, this, &ExtractionDialog::writeSettings);
0082 }
0083 
0084 void ExtractionDialog::slotAccepted()
0085 {
0086     // If an item is selected, enter it if it exists and is a dir.
0087     if (!fileWidget->dirOperator()->selectedItems().isEmpty()) {
0088         QFileInfo fi(fileWidget->dirOperator()->selectedItems().urlList().first().path());
0089         if (fi.isDir() && fi.exists()) {
0090             fileWidget->locationEdit()->clear();
0091             fileWidget->setUrl(QUrl::fromLocalFile(fi.absoluteFilePath()));
0092         }
0093         return;
0094     }
0095 
0096     // We extract to baseUrl().
0097     const QString destinationPath = fileWidget->baseUrl().path();
0098 
0099     // If extracting to a subfolder, we need to do some checks.
0100     if (extractToSubfolder()) {
0101         // Check if subfolder contains slashes.
0102         if (subfolder().contains(QLatin1String("/"))) {
0103             KMessageBox::error(this, i18n("The subfolder name may not contain the character '/'."));
0104             return;
0105         }
0106 
0107         // Handle existing subfolder.
0108         const QString pathWithSubfolder = destinationPath + subfolder();
0109         while (1) {
0110             if (QDir(pathWithSubfolder).exists()) {
0111                 if (QFileInfo(pathWithSubfolder).isDir()) {
0112                     int overwrite = KMessageBox::questionTwoActionsCancel(
0113                         this,
0114                         xi18nc("@info", "The folder <filename>%1</filename> already exists. Are you sure you want to extract here?", pathWithSubfolder),
0115                         i18n("Folder exists"),
0116                         KGuiItem(i18n("Extract here")),
0117                         KGuiItem(i18n("Retry")));
0118 
0119                     if (overwrite == KMessageBox::SecondaryAction) {
0120                         // The user clicked Retry.
0121                         continue;
0122                     } else if (overwrite == KMessageBox::Cancel) {
0123                         return;
0124                     }
0125                 } else {
0126                     KMessageBox::detailedError(this,
0127                                                xi18nc("@info", "The folder <filename>%1</filename> could not be created.", subfolder()),
0128                                                xi18nc("@info", "<filename>%1</filename> already exists, but is not a folder.", subfolder()));
0129                     return;
0130                 }
0131             } else if (!QDir().mkdir(pathWithSubfolder)) {
0132                 KMessageBox::detailedError(this,
0133                                            xi18nc("@info", "The folder <filename>%1</filename> could not be created.", subfolder()),
0134                                            i18n("Please check your permissions to create it."));
0135                 return;
0136             }
0137             break;
0138         }
0139     }
0140 
0141     // Add new destination value to arkrc for quickExtractMenu.
0142     KConfigGroup conf(KSharedConfig::openConfig(), QStringLiteral("ExtractDialog"));
0143     QStringList destHistory = conf.readPathEntry("DirHistory", QStringList());
0144     destHistory.prepend(destinationPath);
0145     destHistory.removeDuplicates();
0146     if (destHistory.size() > 10) {
0147         destHistory.removeLast();
0148     }
0149     conf.writePathEntry("DirHistory", destHistory);
0150 
0151     fileWidget->accept();
0152     accept();
0153 }
0154 
0155 void ExtractionDialog::loadSettings()
0156 {
0157     setOpenDestinationFolderAfterExtraction(ArkSettings::openDestinationFolderAfterExtraction());
0158     setCloseAfterExtraction(ArkSettings::closeAfterExtraction());
0159     setPreservePaths(ArkSettings::preservePaths());
0160 }
0161 
0162 void ExtractionDialog::setExtractToSubfolder(bool extractToSubfolder)
0163 {
0164     m_ui->singleFolderGroup->setChecked(extractToSubfolder && ArkSettings::extractToSubfolder());
0165 }
0166 
0167 void ExtractionDialog::batchModeOption()
0168 {
0169     m_ui->autoSubfolders->show();
0170     m_ui->autoSubfolders->setEnabled(true);
0171     m_ui->singleFolderGroup->hide();
0172     m_ui->extractAllLabel->setText(i18n("Extract multiple archives"));
0173 }
0174 
0175 void ExtractionDialog::setSubfolder(const QString &subfolder)
0176 {
0177     m_ui->subfolder->setText(subfolder);
0178 }
0179 
0180 QString ExtractionDialog::subfolder() const
0181 {
0182     return m_ui->subfolder->text();
0183 }
0184 
0185 void ExtractionDialog::setBusyGui()
0186 {
0187     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0188     fileWidget->setEnabled(false);
0189     m_ui->setEnabled(false);
0190     // TODO: tell the user why the dialog is busy (e.g. "archive is being loaded").
0191 }
0192 
0193 void ExtractionDialog::setReadyGui()
0194 {
0195     QApplication::restoreOverrideCursor();
0196     fileWidget->setEnabled(true);
0197     m_ui->setEnabled(true);
0198 }
0199 
0200 ExtractionDialog::~ExtractionDialog()
0201 {
0202     delete m_ui;
0203     m_ui = nullptr;
0204 }
0205 
0206 void ExtractionDialog::setShowSelectedFiles(bool value)
0207 {
0208     if (value) {
0209         m_ui->filesToExtractGroupBox->show();
0210         m_ui->gridLayout->removeItem(m_ui->gridLayoutVerticalSpacer);
0211         delete m_ui->gridLayoutVerticalSpacer;
0212         m_ui->selectedFilesButton->setChecked(true);
0213         m_ui->extractAllLabel->hide();
0214     } else {
0215         m_ui->filesToExtractGroupBox->hide();
0216         m_ui->gridLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding), 6, 0);
0217         m_ui->selectedFilesButton->setChecked(false);
0218         m_ui->extractAllLabel->show();
0219     }
0220 }
0221 
0222 bool ExtractionDialog::extractAllFiles() const
0223 {
0224     return m_ui->allFilesButton->isChecked();
0225 }
0226 
0227 void ExtractionDialog::setAutoSubfolder(bool value)
0228 {
0229     m_ui->autoSubfolders->setChecked(value);
0230 }
0231 
0232 bool ExtractionDialog::autoSubfolders() const
0233 {
0234     return m_ui->autoSubfolders->isChecked();
0235 }
0236 
0237 bool ExtractionDialog::extractToSubfolder() const
0238 {
0239     return m_ui->singleFolderGroup->isChecked();
0240 }
0241 
0242 void ExtractionDialog::setOpenDestinationFolderAfterExtraction(bool value)
0243 {
0244     m_ui->openFolderCheckBox->setChecked(value);
0245 }
0246 
0247 void ExtractionDialog::setCloseAfterExtraction(bool value)
0248 {
0249     m_ui->closeAfterExtraction->setChecked(value);
0250 }
0251 
0252 void ExtractionDialog::setPreservePaths(bool value)
0253 {
0254     m_ui->preservePaths->setChecked(value);
0255 }
0256 
0257 bool ExtractionDialog::preservePaths() const
0258 {
0259     return m_ui->preservePaths->isChecked();
0260 }
0261 
0262 bool ExtractionDialog::openDestinationAfterExtraction() const
0263 {
0264     return m_ui->openFolderCheckBox->isChecked();
0265 }
0266 
0267 bool ExtractionDialog::closeAfterExtraction() const
0268 {
0269     return m_ui->closeAfterExtraction->isChecked();
0270 }
0271 
0272 QUrl ExtractionDialog::destinationDirectory() const
0273 {
0274     if (extractToSubfolder()) {
0275         QUrl subUrl = fileWidget->baseUrl();
0276         if (subUrl.path().endsWith(QDir::separator())) {
0277             subUrl.setPath(subUrl.path() + subfolder());
0278         } else {
0279             subUrl.setPath(subUrl.path() + QDir::separator() + subfolder());
0280         }
0281 
0282         return subUrl;
0283     } else {
0284         return fileWidget->baseUrl();
0285     }
0286 }
0287 
0288 void ExtractionDialog::writeSettings()
0289 {
0290     ArkSettings::setOpenDestinationFolderAfterExtraction(openDestinationAfterExtraction());
0291     ArkSettings::setCloseAfterExtraction(closeAfterExtraction());
0292     ArkSettings::setPreservePaths(preservePaths());
0293     ArkSettings::self()->save();
0294 
0295     // Save dialog window size
0296     KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("ExtractDialog"));
0297     KWindowConfig::saveWindowSize(windowHandle(), group, KConfigBase::Persistent);
0298 }
0299 
0300 void ExtractionDialog::setCurrentUrl(const QUrl &url)
0301 {
0302     fileWidget->setUrl(url);
0303 }
0304 
0305 void ExtractionDialog::restoreWindowSize()
0306 {
0307     // Restore window size from config file, needs a windowHandle so must be called after show()
0308     KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("ExtractDialog"));
0309     KWindowConfig::restoreWindowSize(windowHandle(), group);
0310 }
0311 
0312 }
0313 
0314 #include "extractiondialog.moc"
0315 #include "moc_extractiondialog.cpp"