[kde-doc-english] [ark/Applications/16.08] /: Add multi-volume support

Ragnar Thomsen rthomsen6 at gmail.com
Thu Jul 21 18:53:55 UTC 2016


Git commit bf6425d17578a31e01916e974558a8f7da09cc27 by Ragnar Thomsen.
Committed on 21/07/2016 at 18:45.
Pushed by rthomsen into branch 'Applications/16.08'.

Add multi-volume support

Support for creating multi-volume rar, 7z and zip archives was
implemented. A QDoubleSpinBox was added to CompressionOptionsWidget
which allows setting the volume size in megabytes between 0.1 to 1000.
The size in megabytes is converted by CompressionOptionsWidget to
kilobytes because 7z doesn't support volume sizes with decimals.

Creating a multi-volume archive changes the archive name
(name.part1.rar, name.7z.001, name.zip.001) so we need to re-open the
archive (the first volume) after adding files.

We only support adding files once, so add/delete actions are disabled in
Part if archive is multi-volume and non-empty.

FEATURE: 124180
FIXED-IN: 16.08.0
Differential Revision: D2194
GUI:

M  +4    -0    app/mainwindow.cpp
M  +1    -1    autotests/plugins/cli7zplugin/cli7ztest.cpp
M  +1    -1    autotests/plugins/clirarplugin/clirartest.cpp
M  +1    -1    autotests/plugins/clizipplugin/cliziptest.cpp
M  +2    -0    kerfuffle/adddialog.cpp
M  +11   -0    kerfuffle/archive_kerfuffle.cpp
M  +5    -2    kerfuffle/archive_kerfuffle.h
M  +11   -3    kerfuffle/archiveformat.cpp
M  +4    -1    kerfuffle/archiveformat.h
M  +10   -0    kerfuffle/archiveinterface.cpp
M  +3    -1    kerfuffle/archiveinterface.h
M  +45   -3    kerfuffle/cliinterface.cpp
M  +8    -2    kerfuffle/cliinterface.h
M  +42   -0    kerfuffle/compressionoptionswidget.cpp
M  +4    -0    kerfuffle/compressionoptionswidget.h
M  +71   -4    kerfuffle/compressionoptionswidget.ui
M  +5    -0    kerfuffle/createdialog.cpp
M  +1    -0    kerfuffle/createdialog.h
M  +20   -1    part/part.cpp
M  +4    -1    plugins/cli7zplugin/cliplugin.cpp
M  +4    -2    plugins/cli7zplugin/kerfuffle_cli7z.json.cmake
M  +10   -4    plugins/clirarplugin/cliplugin.cpp
M  +2    -1    plugins/clirarplugin/kerfuffle_clirar.json.cmake

http://commits.kde.org/ark/bf6425d17578a31e01916e974558a8f7da09cc27

diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp
index 7379f59..18d7fbb 100644
--- a/app/mainwindow.cpp
+++ b/app/mainwindow.cpp
@@ -300,6 +300,10 @@ void MainWindow::newArchive()
         if (dialog.data()->compressionLevel() > -1) {
             m_openArgs.metaData()[QStringLiteral("compressionLevel")] = QString::number(dialog.data()->compressionLevel());
         }
+        if (dialog.data()->volumeSize() > 0) {
+            qCDebug(ARK) << "Setting volume size:" << QString::number(dialog.data()->volumeSize());
+            m_openArgs.metaData()[QStringLiteral("volumeSize")] = QString::number(dialog.data()->volumeSize());
+        }
         m_openArgs.metaData()[QStringLiteral("encryptionPassword")] = password;
 
         if (dialog.data()->isHeaderEncryptionEnabled()) {
diff --git a/autotests/plugins/cli7zplugin/cli7ztest.cpp b/autotests/plugins/cli7zplugin/cli7ztest.cpp
index a326cd0..1f73dd6 100644
--- a/autotests/plugins/cli7zplugin/cli7ztest.cpp
+++ b/autotests/plugins/cli7zplugin/cli7ztest.cpp
@@ -305,7 +305,7 @@ void Cli7zTest::testAddArgs()
     QFETCH(bool, encryptHeader);
     QFETCH(int, compressionLevel);
 
-    QStringList replacedArgs = plugin->substituteAddVariables(addArgs, {}, password, encryptHeader, compressionLevel);
+    QStringList replacedArgs = plugin->substituteAddVariables(addArgs, {}, password, encryptHeader, compressionLevel, 0);
 
     QFETCH(QStringList, expectedArgs);
     QCOMPARE(replacedArgs, expectedArgs);
diff --git a/autotests/plugins/clirarplugin/clirartest.cpp b/autotests/plugins/clirarplugin/clirartest.cpp
index 50ed482..302e686 100644
--- a/autotests/plugins/clirarplugin/clirartest.cpp
+++ b/autotests/plugins/clirarplugin/clirartest.cpp
@@ -334,7 +334,7 @@ void CliRarTest::testAddArgs()
     QFETCH(bool, encryptHeader);
     QFETCH(int, compressionLevel);
 
-    QStringList replacedArgs = rarPlugin->substituteAddVariables(addArgs, {}, password, encryptHeader, compressionLevel);
+    QStringList replacedArgs = rarPlugin->substituteAddVariables(addArgs, {}, password, encryptHeader, compressionLevel, 0);
 
     QFETCH(QStringList, expectedArgs);
     QCOMPARE(replacedArgs, expectedArgs);
diff --git a/autotests/plugins/clizipplugin/cliziptest.cpp b/autotests/plugins/clizipplugin/cliziptest.cpp
index 5f066d8..56333a6 100644
--- a/autotests/plugins/clizipplugin/cliziptest.cpp
+++ b/autotests/plugins/clizipplugin/cliziptest.cpp
@@ -106,7 +106,7 @@ void CliZipTest::testAddArgs()
     QFETCH(QString, password);
     QFETCH(int, compressionLevel);
 
-    QStringList replacedArgs = plugin->substituteAddVariables(addArgs, {}, password, false, compressionLevel);
+    QStringList replacedArgs = plugin->substituteAddVariables(addArgs, {}, password, false, compressionLevel, 0);
 
     QFETCH(QStringList, expectedArgs);
     QCOMPARE(replacedArgs, expectedArgs);
diff --git a/kerfuffle/adddialog.cpp b/kerfuffle/adddialog.cpp
index 882f145..a86c626 100644
--- a/kerfuffle/adddialog.cpp
+++ b/kerfuffle/adddialog.cpp
@@ -88,6 +88,7 @@ QStringList AddDialog::selectedFiles() const
 
 CompressionOptions AddDialog::compressionOptions() const
 {
+    qCDebug(ARK) << "Returning with options:" << m_compOptions;
     return m_compOptions;
 }
 
@@ -100,6 +101,7 @@ void AddDialog::slotOpenOptions()
     CompressionOptionsWidget *optionsWidget = new CompressionOptionsWidget(optionsDialog, m_compOptions);
     optionsWidget->setMimeType(m_mimeType);
     optionsWidget->setEncryptionVisible(false);
+    optionsWidget->collapsibleMultiVolume->setVisible(false);
     optionsWidget->collapsibleCompression->expand();
     vlayout->addWidget(optionsWidget);
 
diff --git a/kerfuffle/archive_kerfuffle.cpp b/kerfuffle/archive_kerfuffle.cpp
index 6d42a18..b8f55ca 100644
--- a/kerfuffle/archive_kerfuffle.cpp
+++ b/kerfuffle/archive_kerfuffle.cpp
@@ -126,6 +126,7 @@ Archive::Archive(ReadOnlyArchiveInterface *archiveInterface, bool isReadOnly, QO
         , m_hasBeenListed(false)
         , m_isReadOnly(isReadOnly)
         , m_isSingleFolderArchive(false)
+        , m_isMultiVolume(false)
         , m_extractedFilesSize(0)
         , m_error(NoError)
         , m_encryptionType(Unencrypted)
@@ -232,6 +233,11 @@ bool Archive::isMultiVolume() const
     return m_iface->isMultiVolume();
 }
 
+void Archive::setMultiVolume(bool value)
+{
+    m_iface->setMultiVolume(value);
+}
+
 int Archive::numberOfVolumes() const
 {
     return m_iface->numberOfVolumes();
@@ -495,4 +501,9 @@ CompressionOptions Archive::compressionOptions() const
     return m_compOptions;
 }
 
+QString Archive::multiVolumeName() const
+{
+    return m_iface->multiVolumeName();
+}
+
 } // namespace Kerfuffle
diff --git a/kerfuffle/archive_kerfuffle.h b/kerfuffle/archive_kerfuffle.h
index 07c3209..8c92169 100644
--- a/kerfuffle/archive_kerfuffle.h
+++ b/kerfuffle/archive_kerfuffle.h
@@ -151,7 +151,7 @@ class KERFUFFLE_EXPORT Archive : public QObject
     Q_PROPERTY(QMimeType mimeType READ mimeType CONSTANT)
     Q_PROPERTY(bool isReadOnly READ isReadOnly CONSTANT)
     Q_PROPERTY(bool isSingleFolderArchive READ isSingleFolderArchive)
-    Q_PROPERTY(bool isMultiVolume READ isMultiVolume)
+    Q_PROPERTY(bool isMultiVolume READ isMultiVolume WRITE setMultiVolume)
     Q_PROPERTY(bool numberOfVolumes READ numberOfVolumes)
     Q_PROPERTY(EncryptionType encryptionType READ encryptionType)
     Q_PROPERTY(qulonglong numberOfFiles READ numberOfFiles)
@@ -176,8 +176,9 @@ public:
     QMimeType mimeType();
     bool isReadOnly() const;
     bool isSingleFolderArchive();
-    bool hasComment() const;
     bool isMultiVolume() const;
+    void setMultiVolume(bool value);
+    bool hasComment() const;
     int numberOfVolumes() const;
     EncryptionType encryptionType();
     QString password() const;
@@ -188,6 +189,7 @@ public:
     QString subfolderName();
     void setCompressionOptions(const CompressionOptions &opts);
     CompressionOptions compressionOptions() const;
+    QString multiVolumeName() const;
 
     static Archive *create(const QString &fileName, QObject *parent = 0);
     static Archive *create(const QString &fileName, const QString &fixedMimeType, QObject *parent = 0);
@@ -257,6 +259,7 @@ private:
     bool m_hasBeenListed;
     bool m_isReadOnly;
     bool m_isSingleFolderArchive;
+    bool m_isMultiVolume;
 
     QString m_subfolderName;
     qulonglong m_extractedFilesSize;
diff --git a/kerfuffle/archiveformat.cpp b/kerfuffle/archiveformat.cpp
index c7c7817..9d36866 100644
--- a/kerfuffle/archiveformat.cpp
+++ b/kerfuffle/archiveformat.cpp
@@ -39,14 +39,16 @@ ArchiveFormat::ArchiveFormat(const QMimeType& mimeType,
                              int maxCompLevel,
                              int defaultCompLevel,
                              bool supportsWriteComment,
-                             bool supportsTesting) :
+                             bool supportsTesting,
+                             bool supportsMultiVolume) :
     m_mimeType(mimeType),
     m_encryptionType(encryptionType),
     m_minCompressionLevel(minCompLevel),
     m_maxCompressionLevel(maxCompLevel),
     m_defaultCompressionLevel(defaultCompLevel),
     m_supportsWriteComment(supportsWriteComment),
-    m_supportsTesting(supportsTesting)
+    m_supportsTesting(supportsTesting),
+    m_supportsMultiVolume(supportsMultiVolume)
 {
 }
 
@@ -66,6 +68,7 @@ ArchiveFormat ArchiveFormat::fromMetadata(const QMimeType& mimeType, const KPlug
 
         bool supportsWriteComment = formatProps[QStringLiteral("SupportsWriteComment")].toBool();
         bool supportsTesting = formatProps[QStringLiteral("SupportsTesting")].toBool();
+        bool supportsMultiVolume = formatProps[QStringLiteral("SupportsMultiVolume")].toBool();
 
         Archive::EncryptionType encType = Archive::Unencrypted;
         if (formatProps[QStringLiteral("HeaderEncryption")].toBool()) {
@@ -74,7 +77,7 @@ ArchiveFormat ArchiveFormat::fromMetadata(const QMimeType& mimeType, const KPlug
             encType = Archive::Encrypted;
         }
 
-        return ArchiveFormat(mimeType, encType, minCompLevel, maxCompLevel, defaultCompLevel, supportsWriteComment, supportsTesting);
+        return ArchiveFormat(mimeType, encType, minCompLevel, maxCompLevel, defaultCompLevel, supportsWriteComment, supportsTesting, supportsMultiVolume);
     }
 
     return ArchiveFormat();
@@ -115,4 +118,9 @@ bool ArchiveFormat::supportsTesting() const
     return m_supportsTesting;
 }
 
+bool ArchiveFormat::supportsMultiVolume() const
+{
+    return m_supportsMultiVolume;
+}
+
 }
diff --git a/kerfuffle/archiveformat.h b/kerfuffle/archiveformat.h
index bd2b03a..f25e23b 100644
--- a/kerfuffle/archiveformat.h
+++ b/kerfuffle/archiveformat.h
@@ -43,7 +43,8 @@ public:
                            int maxCompLevel,
                            int defaultCompLevel,
                            bool supportsWriteComment,
-                           bool supportsTesting);
+                           bool supportsTesting,
+                           bool suppportsMultiVolume);
 
     /**
      * @return The archive format of the given @p mimeType, according to the given @p metadata.
@@ -65,6 +66,7 @@ public:
     int defaultCompressionLevel() const;
     bool supportsWriteComment() const;
     bool supportsTesting() const;
+    bool supportsMultiVolume() const;
 
 private:
     QMimeType m_mimeType;
@@ -74,6 +76,7 @@ private:
     int m_defaultCompressionLevel;
     bool m_supportsWriteComment;
     bool m_supportsTesting;
+    bool m_supportsMultiVolume;
 };
 
 }
diff --git a/kerfuffle/archiveinterface.cpp b/kerfuffle/archiveinterface.cpp
index 30afb05..abd68fe 100644
--- a/kerfuffle/archiveinterface.cpp
+++ b/kerfuffle/archiveinterface.cpp
@@ -120,11 +120,21 @@ bool ReadOnlyArchiveInterface::isMultiVolume() const
     return m_isMultiVolume;
 }
 
+void ReadOnlyArchiveInterface::setMultiVolume(bool value)
+{
+    m_isMultiVolume = value;
+}
+
 int ReadOnlyArchiveInterface::numberOfVolumes() const
 {
     return m_numberOfVolumes;
 }
 
+QString ReadOnlyArchiveInterface::multiVolumeName() const
+{
+    return filename();
+}
+
 ReadWriteArchiveInterface::ReadWriteArchiveInterface(QObject *parent, const QVariantList & args)
         : ReadOnlyArchiveInterface(parent, args)
 {
diff --git a/kerfuffle/archiveinterface.h b/kerfuffle/archiveinterface.h
index e61f5d6..77e3bd2 100644
--- a/kerfuffle/archiveinterface.h
+++ b/kerfuffle/archiveinterface.h
@@ -106,6 +106,8 @@ public:
     virtual bool doResume();
 
     bool isHeaderEncryptionEnabled() const;
+    virtual QString multiVolumeName() const;
+    void setMultiVolume(bool value);
 
 signals:
     void cancelled();
@@ -129,7 +131,6 @@ protected:
     void setCorrupt(bool isCorrupt);
     bool isCorrupt() const;
     QString m_comment;
-    bool m_isMultiVolume;
     int m_numberOfVolumes;
 
 private:
@@ -138,6 +139,7 @@ private:
     bool m_waitForFinishedSignal;
     bool m_isHeaderEncryptionEnabled;
     bool m_isCorrupt;
+    bool m_isMultiVolume;
 };
 
 class KERFUFFLE_EXPORT ReadWriteArchiveInterface: public ReadOnlyArchiveInterface
diff --git a/kerfuffle/cliinterface.cpp b/kerfuffle/cliinterface.cpp
index 9f445f8..ae89e8a 100644
--- a/kerfuffle/cliinterface.cpp
+++ b/kerfuffle/cliinterface.cpp
@@ -46,6 +46,7 @@
 #include <QDirIterator>
 #include <QEventLoop>
 #include <QFile>
+#include <QMimeDatabase>
 #include <QProcess>
 #include <QRegularExpression>
 #include <QStandardPaths>
@@ -182,12 +183,14 @@ bool CliInterface::addFiles(const QStringList & files, const CompressionOptions&
     }
 
     int compLevel = options.value(QStringLiteral("CompressionLevel"), -1).toInt();
+    ulong volumeSize = options.value(QStringLiteral("VolumeSize"), 0).toULongLong();
 
     const auto args = substituteAddVariables(m_param.value(AddArgs).toStringList(),
                                              files,
                                              password(),
                                              isHeaderEncryptionEnabled(),
-                                             compLevel);
+                                             compLevel,
+                                             volumeSize);
 
     if (!runProcess(m_param.value(AddProgram).toStringList(), args)) {
         return false;
@@ -303,7 +306,7 @@ void CliInterface::processFinished(int exitCode, QProcess::ExitStatus exitStatus
         }
     }
 
-    if (m_operationMode == Add) {
+    if (m_operationMode == Add && !isMultiVolume()) {
         list();
     } else if (m_operationMode == List && isCorrupt()) {
         Kerfuffle::LoadCorruptQuery query(filename());
@@ -635,7 +638,7 @@ QStringList CliInterface::substituteCopyVariables(const QStringList &extractArgs
     return args;
 }
 
-QStringList CliInterface::substituteAddVariables(const QStringList &addArgs, const QStringList &files, const QString &password, bool encryptHeader, int compLevel)
+QStringList CliInterface::substituteAddVariables(const QStringList &addArgs, const QStringList &files, const QString &password, bool encryptHeader, int compLevel, ulong volumeSize)
 {
     // Required if we call this function from unit tests.
     cacheParameterList();
@@ -659,6 +662,11 @@ QStringList CliInterface::substituteAddVariables(const QStringList &addArgs, con
             continue;
         }
 
+        if (arg == QLatin1String("$MultiVolumeSwitch")) {
+            args << multiVolumeSwitch(volumeSize);
+            continue;
+        }
+
         if (arg == QLatin1String("$Files")) {
             args << files;
             continue;
@@ -832,6 +840,24 @@ QString CliInterface::compressionLevelSwitch(int level) const
     return compLevelSwitch;
 }
 
+QString CliInterface::multiVolumeSwitch(ulong volumeSize) const
+{
+    // The maximum value we allow in the QDoubleSpinBox is 1000MB. Converted to
+    // KB this is 1024000.
+    if (volumeSize <= 0 || volumeSize > 1024000) {
+        return QString();
+    }
+
+    Q_ASSERT(m_param.contains(MultiVolumeSwitch));
+
+    QString multiVolumeSwitch = m_param.value(MultiVolumeSwitch).toString();
+    Q_ASSERT(!multiVolumeSwitch.isEmpty());
+
+    multiVolumeSwitch.replace(QLatin1String("$VolumeSize"), QString::number(volumeSize));
+
+    return multiVolumeSwitch;
+}
+
 QStringList CliInterface::copyFilesList(const QVariantList& files) const
 {
     QStringList filesList;
@@ -1261,4 +1287,20 @@ bool CliInterface::addComment(const QString &comment)
     return true;
 }
 
+QString CliInterface::multiVolumeName() const
+{
+    QString oldSuffix = QMimeDatabase().suffixForFileName(filename());
+    QString name;
+
+    foreach (const QString &multiSuffix, m_param.value(MultiVolumeSuffix).toStringList()) {
+        QString newSuffix = multiSuffix;
+        newSuffix.replace(QStringLiteral("$Suffix"), oldSuffix);
+        name = filename().remove(oldSuffix).append(newSuffix);
+        if (QFileInfo(name).exists()) {
+            break;
+        }
+    }
+    return name;
+}
+
 }
diff --git a/kerfuffle/cliinterface.h b/kerfuffle/cliinterface.h
index de97c69..6cb5042 100644
--- a/kerfuffle/cliinterface.h
+++ b/kerfuffle/cliinterface.h
@@ -263,7 +263,9 @@ enum CliInterfaceParameters {
     CommentSwitch,
     TestProgram,
     TestArgs,
-    TestPassedPattern
+    TestPassedPattern,
+    MultiVolumeSwitch,
+    MultiVolumeSuffix
 };
 
 typedef QHash<int, QVariant> ParameterList;
@@ -310,7 +312,7 @@ public:
 
     QStringList substituteListVariables(const QStringList &listArgs, const QString &password);
     QStringList substituteCopyVariables(const QStringList &extractArgs, const QVariantList &files, bool preservePaths, const QString &password);
-    QStringList substituteAddVariables(const QStringList &addArgs, const QStringList &files, const QString &password, bool encryptHeader, int compLevel);
+    QStringList substituteAddVariables(const QStringList &addArgs, const QStringList &files, const QString &password, bool encryptHeader, int compLevel, ulong volumeSize);
     QStringList substituteDeleteVariables(const QStringList &deleteArgs, const QVariantList &files, const QString &password);
     QStringList substituteCommentVariables(const QStringList &commentArgs, const QString &commentFile);
     QStringList substituteTestVariables(const QStringList &testArgs, const QString &password);
@@ -335,11 +337,15 @@ public:
      */
     QString compressionLevelSwitch(int level) const;
 
+    QString multiVolumeSwitch(ulong volumeSize) const;
+
     /**
      * @return The list of selected files to extract.
      */
     QStringList copyFilesList(const QVariantList& files) const;
 
+    QString multiVolumeName() const;
+
 protected:
 
     virtual void handleLine(const QString& line);
diff --git a/kerfuffle/compressionoptionswidget.cpp b/kerfuffle/compressionoptionswidget.cpp
index 4340009..8238071 100644
--- a/kerfuffle/compressionoptionswidget.cpp
+++ b/kerfuffle/compressionoptionswidget.cpp
@@ -47,12 +47,24 @@ CompressionOptionsWidget::CompressionOptionsWidget(QWidget *parent,
     KColorScheme colorScheme(QPalette::Active, KColorScheme::View);
     pwdWidget->setBackgroundWarningColor(colorScheme.background(KColorScheme::NegativeBackground).color());
     pwdWidget->setPasswordStrengthMeterVisible(false);
+
+    connect(multiVolumeCheckbox, &QCheckBox::stateChanged, this, &CompressionOptionsWidget::slotMultiVolumeChecked);
+
+    if (m_opts.contains(QStringLiteral("VolumeSize"))) {
+        multiVolumeCheckbox->setChecked(true);
+        // Convert from kilobytes.
+        volumeSizeSpinbox->setValue(m_opts.value(QStringLiteral("VolumeSize")).toDouble() / 1024);
+    }
 }
 
 CompressionOptions CompressionOptionsWidget::commpressionOptions() const
 {
     CompressionOptions opts;
     opts[QStringLiteral("CompressionLevel")] = compLevelSlider->value();
+    if (multiVolumeCheckbox->isChecked()) {
+        // Convert to kilobytes.
+        opts[QStringLiteral("VolumeSize")] = QString::number(volumeSize());
+    }
 
     return opts;
 }
@@ -62,6 +74,16 @@ int CompressionOptionsWidget::compressionLevel() const
     return compLevelSlider->value();
 }
 
+ulong CompressionOptionsWidget::volumeSize() const
+{
+    if (collapsibleMultiVolume->isEnabled() && multiVolumeCheckbox->isChecked()) {
+        // Convert to kilobytes.
+        return volumeSizeSpinbox->value() * 1024;
+    } else {
+        return 0;
+    }
+}
+
 void CompressionOptionsWidget::setEncryptionVisible(bool visible)
 {
     collapsibleEncryption->setVisible(visible);
@@ -122,6 +144,15 @@ void CompressionOptionsWidget::updateWidgets()
             compLevelSlider->setValue(archiveFormat.defaultCompressionLevel());
         }
     }
+
+    if (archiveFormat.supportsMultiVolume()) {
+        collapsibleMultiVolume->setEnabled(true);
+        collapsibleMultiVolume->setToolTip(QString());
+    } else {
+        collapsibleMultiVolume->setEnabled(false);
+        collapsibleMultiVolume->setToolTip(i18n("The %1 format does not support multi-volume archives.",
+                                                m_mimetype.comment()));
+    }
 }
 
 void CompressionOptionsWidget::setMimeType(const QMimeType &mimeType)
@@ -155,4 +186,15 @@ KNewPasswordWidget::PasswordStatus CompressionOptionsWidget::passwordStatus() co
     return pwdWidget->passwordStatus();
 }
 
+void CompressionOptionsWidget::slotMultiVolumeChecked(int state)
+{
+    if (state == Qt::Checked) {
+        lblVolumeSize->setEnabled(true);
+        volumeSizeSpinbox->setEnabled(true);
+    } else {
+        lblVolumeSize->setEnabled(false);
+        volumeSizeSpinbox->setEnabled(false);
+    }
+}
+
 }
diff --git a/kerfuffle/compressionoptionswidget.h b/kerfuffle/compressionoptionswidget.h
index 350aa7a..00b95f8 100644
--- a/kerfuffle/compressionoptionswidget.h
+++ b/kerfuffle/compressionoptionswidget.h
@@ -45,6 +45,7 @@ public:
     explicit CompressionOptionsWidget(QWidget *parent = Q_NULLPTR,
                                       const CompressionOptions &opts = QHash<QString, QVariant>());
     int compressionLevel() const;
+    ulong volumeSize() const;
     QString password() const;
     CompressionOptions commpressionOptions() const;
     bool isEncryptionAvailable() const;
@@ -61,6 +62,9 @@ private:
 
     QMimeType m_mimetype;
     CompressionOptions m_opts;
+
+private slots:
+    void slotMultiVolumeChecked(int state);
 };
 }
 
diff --git a/kerfuffle/compressionoptionswidget.ui b/kerfuffle/compressionoptionswidget.ui
index b0a36f4..3a17156 100644
--- a/kerfuffle/compressionoptionswidget.ui
+++ b/kerfuffle/compressionoptionswidget.ui
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>384</width>
-    <height>62</height>
+    <width>401</width>
+    <height>90</height>
    </rect>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
@@ -106,7 +106,7 @@
      </property>
      <layout class="QVBoxLayout" name="verticalLayout_3">
       <item>
-       <widget class="KNewPasswordWidget" name="pwdWidget" native="true">
+       <widget class="KNewPasswordWidget" name="pwdWidget">
         <property name="enabled">
          <bool>false</bool>
         </property>
@@ -131,6 +131,73 @@
      </layout>
     </widget>
    </item>
+   <item>
+    <widget class="KCollapsibleGroupBox" name="collapsibleMultiVolume">
+     <property name="title">
+      <string>Multi-volume Archive</string>
+     </property>
+     <property name="expanded">
+      <bool>false</bool>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item>
+       <widget class="QCheckBox" name="multiVolumeCheckbox">
+        <property name="text">
+         <string>Create multi-volume archive</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QFormLayout" name="formLayout">
+        <property name="verticalSpacing">
+         <number>0</number>
+        </property>
+        <property name="leftMargin">
+         <number>30</number>
+        </property>
+        <item row="1" column="0">
+         <widget class="QLabel" name="lblVolumeSize">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="text">
+           <string>Volume size:</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="1">
+         <widget class="QDoubleSpinBox" name="volumeSizeSpinbox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="suffix">
+           <string> megabytes</string>
+          </property>
+          <property name="decimals">
+           <number>1</number>
+          </property>
+          <property name="minimum">
+           <double>0.100000000000000</double>
+          </property>
+          <property name="maximum">
+           <double>1000.000000000000000</double>
+          </property>
+          <property name="singleStep">
+           <double>0.500000000000000</double>
+          </property>
+          <property name="value">
+           <double>1.000000000000000</double>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
   </layout>
  </widget>
  <customwidgets>
@@ -143,7 +210,7 @@
   <customwidget>
    <class>KNewPasswordWidget</class>
    <extends>QWidget</extends>
-   <header location="global">KNewPasswordWidget</header>
+   <header>knewpasswordwidget.h</header>
    <container>1</container>
   </customwidget>
  </customwidgets>
diff --git a/kerfuffle/createdialog.cpp b/kerfuffle/createdialog.cpp
index dd21d88..2078547 100644
--- a/kerfuffle/createdialog.cpp
+++ b/kerfuffle/createdialog.cpp
@@ -151,6 +151,11 @@ int CreateDialog::compressionLevel() const
     return m_ui->optionsWidget->compressionLevel();
 }
 
+ulong CreateDialog::volumeSize() const
+{
+    return m_ui->optionsWidget->volumeSize();
+}
+
 QString CreateDialog::password() const
 {
     return m_ui->optionsWidget->password();
diff --git a/kerfuffle/createdialog.h b/kerfuffle/createdialog.h
index 4ba3d40..302fe9c 100644
--- a/kerfuffle/createdialog.h
+++ b/kerfuffle/createdialog.h
@@ -59,6 +59,7 @@ public:
     QMimeType currentMimeType() const;
     bool setMimeType(const QString &mimeTypeName);
     int compressionLevel() const;
+    ulong volumeSize() const;
 
     /**
      * @return Whether the user can encrypt the new archive.
diff --git a/part/part.cpp b/part/part.cpp
index ee83b8c..5bb1acf 100644
--- a/part/part.cpp
+++ b/part/part.cpp
@@ -409,7 +409,8 @@ void Part::setupActions()
 
 void Part::updateActions()
 {
-    bool isWritable = m_model->archive() && !m_model->archive()->isReadOnly();
+    bool isWritable = m_model->archive() && !m_model->archive()->isReadOnly() &&
+                      !(m_model->rowCount() > 0 && m_model->archive()->isMultiVolume());
     bool isDirectory = m_model->entryForIndex(m_view->selectionModel()->currentIndex())[IsDirectory].toBool();
     int selectedEntriesCount = m_view->selectionModel()->selectedRows().count();
 
@@ -663,6 +664,10 @@ bool Part::openFile()
 
     Q_ASSERT(archive->isValid());
 
+    if (arguments().metaData().contains(QStringLiteral("volumeSize"))) {
+        archive.data()->setMultiVolume(true);
+    }
+
     // Plugin loaded successfully.
     KJob *job = m_model->setArchive(archive.take());
     if (job) {
@@ -1243,6 +1248,9 @@ void Part::slotAddFiles()
         if (arguments().metaData().contains(QStringLiteral("compressionLevel"))) {
             opts[QStringLiteral("CompressionLevel")] = arguments().metaData()[QStringLiteral("compressionLevel")];
         }
+        if (arguments().metaData().contains(QStringLiteral("volumeSize"))) {
+            opts[QStringLiteral("VolumeSize")] = arguments().metaData()[QStringLiteral("volumeSize")];
+        }
         m_model->archive()->setCompressionOptions(opts);
     } else {
         opts = m_model->archive()->compressionOptions();
@@ -1282,6 +1290,17 @@ void Part::slotAddFilesDone(KJob* job)
     } else {
         // Hide the "archive will be created as soon as you add a file" message.
         m_messageWidget->hide();
+
+        // For multi-volume archive, we need to re-open the archive after adding files
+        // because the name changes from e.g name.rar to name.part1.rar.
+        if (m_model->archive()->isMultiVolume()) {
+            qCDebug(ARK) << "Multi-volume archive detected, re-opening...";
+            KParts::OpenUrlArguments args = arguments();
+            args.metaData()[QStringLiteral("createNewArchive")] = QStringLiteral("false");
+            setArguments(args);
+
+            openUrl(QUrl::fromLocalFile(m_model->archive()->multiVolumeName()));
+        }
     }
 }
 
diff --git a/plugins/cli7zplugin/cliplugin.cpp b/plugins/cli7zplugin/cliplugin.cpp
index 0e9f7d2..fa6a59c 100644
--- a/plugins/cli7zplugin/cliplugin.cpp
+++ b/plugins/cli7zplugin/cliplugin.cpp
@@ -79,6 +79,7 @@ ParameterList CliPlugin::parameterList() const
                                    << QStringLiteral("$Archive")
                                    << QStringLiteral("$PasswordSwitch")
                                    << QStringLiteral("$CompressionLevelSwitch")
+                                   << QStringLiteral("$MultiVolumeSwitch")
                                    << QStringLiteral("$Files");
         p[DeleteArgs] = QStringList() << QStringLiteral("d")
                                       << QStringLiteral("$PasswordSwitch")
@@ -104,6 +105,8 @@ ParameterList CliPlugin::parameterList() const
         p[CorruptArchivePatterns] = QStringList() << QStringLiteral("Unexpected end of archive")
                                                   << QStringLiteral("Headers Error");
         p[DiskFullPatterns] = QStringList() << QStringLiteral("No space left on device");
+        p[MultiVolumeSwitch] = QStringLiteral("-v$VolumeSizek");
+        p[MultiVolumeSuffix] = QStringList() << QStringLiteral("$Suffix.001");
     }
 
     return p;
@@ -161,7 +164,7 @@ bool CliPlugin::readListLine(const QString& line)
             } else if (type == QLatin1String("Rar")) {
                 m_archiveType = ArchiveTypeRar;
             } else if (type == QLatin1String("Split")) {
-                m_isMultiVolume = true;
+                setMultiVolume(true);
             } else {
                 // Should not happen
                 qCWarning(ARK) << "Unsupported archive type";
diff --git a/plugins/cli7zplugin/kerfuffle_cli7z.json.cmake b/plugins/cli7zplugin/kerfuffle_cli7z.json.cmake
index 605e5e0..74fba26 100644
--- a/plugins/cli7zplugin/kerfuffle_cli7z.json.cmake
+++ b/plugins/cli7zplugin/kerfuffle_cli7z.json.cmake
@@ -47,13 +47,15 @@
         "CompressionLevelMax": 9,
         "CompressionLevelMin": 0,
         "SupportsTesting": true,
-        "HeaderEncryption": true
+        "HeaderEncryption": true,
+        "SupportsMultiVolume": true
     },
     "application/zip": {
         "CompressionLevelDefault": 5,
         "CompressionLevelMax": 9,
         "CompressionLevelMin": 0,
         "SupportsTesting": true,
-        "Encryption": true
+        "Encryption": true,
+        "SupportsMultiVolume": true
     }
 }
diff --git a/plugins/clirarplugin/cliplugin.cpp b/plugins/clirarplugin/cliplugin.cpp
index 68a21a4..b21a681 100644
--- a/plugins/clirarplugin/cliplugin.cpp
+++ b/plugins/clirarplugin/cliplugin.cpp
@@ -95,6 +95,7 @@ ParameterList CliPlugin::parameterList() const
         p[PasswordSwitch] = QStringList() << QStringLiteral( "-p$Password" );
         p[PasswordHeaderSwitch] = QStringList() << QStringLiteral("-hp$Password");
         p[CompressionLevelSwitch] = QStringLiteral("-m$CompressionLevel");
+        p[MultiVolumeSwitch] = QStringLiteral("-v$VolumeSizek");
         p[DeleteArgs] = QStringList() << QStringLiteral( "d" )
                                       << QStringLiteral( "$PasswordSwitch" )
                                       << QStringLiteral( "$Archive" )
@@ -112,6 +113,7 @@ ParameterList CliPlugin::parameterList() const
                                    << QStringLiteral( "$Archive" )
                                    << QStringLiteral("$PasswordSwitch")
                                    << QStringLiteral("$CompressionLevelSwitch")
+                                   << QStringLiteral("$MultiVolumeSwitch")
                                    << QStringLiteral( "$Files" );
         p[PasswordPromptPattern] = QLatin1String("Enter password \\(will not be echoed\\) for");
         p[WrongPasswordPatterns] = QStringList() << QStringLiteral("password incorrect") << QStringLiteral("wrong password");
@@ -128,6 +130,10 @@ ParameterList CliPlugin::parameterList() const
                                     << QStringLiteral("$Archive")
                                     << QStringLiteral("$PasswordSwitch");
         p[TestPassedPattern] = QStringLiteral("^All OK$");
+        // rar will sometimes create multi-volume archives where first volume is
+        // called name.part1.rar and other times name.part01.rar.
+        p[MultiVolumeSuffix] = QStringList() << QStringLiteral("part01.$Suffix")
+                                             << QStringLiteral("part1.$Suffix");
     }
 
     return p;
@@ -208,8 +214,8 @@ void CliPlugin::handleUnrar5Line(const QString &line) {
             ignoreLines(1, ParseStateEntryDetails);
             if (line.contains(QLatin1String("volume"))) {
                 m_numberOfVolumes++;
-                if (!m_isMultiVolume) {
-                    m_isMultiVolume = true;
+                if (!isMultiVolume()) {
+                    setMultiVolume(true);
                     qCDebug(ARK) << "Multi-volume archive detected";
                 }
             }
@@ -313,8 +319,8 @@ void CliPlugin::handleUnrar4Line(const QString &line) {
 
             if (line.startsWith(QLatin1String("Volume "))) {
                 m_numberOfVolumes++;
-                if (!m_isMultiVolume) {
-                    m_isMultiVolume = true;
+                if (!isMultiVolume()) {
+                    setMultiVolume(true);
                     qCDebug(ARK) << "Multi-volume archive detected";
                 }
             }
diff --git a/plugins/clirarplugin/kerfuffle_clirar.json.cmake b/plugins/clirarplugin/kerfuffle_clirar.json.cmake
index d201a73..96c993d 100644
--- a/plugins/clirarplugin/kerfuffle_clirar.json.cmake
+++ b/plugins/clirarplugin/kerfuffle_clirar.json.cmake
@@ -51,6 +51,7 @@
         "CompressionLevelMin": 0,
         "SupportsWriteComment": true,
         "SupportsTesting": true,
-        "HeaderEncryption": true
+        "HeaderEncryption": true,
+        "SupportsMultiVolume": true
     }
 }


More information about the kde-doc-english mailing list