1 /*****************************************************************************
2 * Copyright (C) 2000 Shie Erlich <krusader@users.sourceforge.net> *
3 * Copyright (C) 2000 Rafi Yanai <krusader@users.sourceforge.net> *
4 * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] *
5 * *
6 * This file is part of Krusader [https://krusader.org]. *
7 * *
8 * Krusader is free software: you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation, either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * Krusader is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with Krusader. If not, see [http://www.gnu.org/licenses/]. *
20 *****************************************************************************/
21
22 #include "packguibase.h"
23
24 // QtCore
25 #include <QVariant>
26 // QtGui
27 #include <QGuiApplication>
28 #include <QImage>
29 #include <QPixmap>
30 #include <QKeyEvent>
31 // QtWidgets
32 #include <QSlider>
33 #include <QCheckBox>
34 #include <QComboBox>
35 #include <QLabel>
36 #include <QLineEdit>
37 #include <QPushButton>
38 #include <QToolButton>
39 #include <QLayout>
40 #include <QSpinBox>
41 #include <QHBoxLayout>
42 #include <QGridLayout>
43 #include <QFrame>
44 #include <QVBoxLayout>
45
46 #include <KConfigCore/KSharedConfig>
47 #include <KI18n/KLocalizedString>
48 #include <KCompletion/KComboBox>
49 #include <KWidgetsAddons/KMessageBox>
50 #include <KIO/Global>
51 #include <KCompletion/KHistoryComboBox>
52 #include <KWidgetsAddons/KStandardGuiItem>
53
54 #include "../defaults.h"
55 #include "../krglobal.h"
56 #include "../icon.h"
57
58 /*
59 * Constructs a PackGUIBase which is a child of 'parent', with the
60 * name 'name' and widget flags set to 'f'
61 *
62 * The dialog will by default be modeless, unless you set 'modal' to
63 * TRUE to construct a modal dialog.
64 */
PackGUIBase(QWidget * parent)65 PackGUIBase::PackGUIBase(QWidget* parent)
66 : QDialog(parent), expanded(false)
67 {
68 KConfigGroup group(krConfig, "Archives");
69
70 setModal(true);
71 resize(430, 140);
72 setWindowTitle(i18n("Pack"));
73 grid = new QGridLayout(this);
74 grid->setSpacing(6);
75 grid->setContentsMargins(11, 11, 11, 11);
76
77 hbox = new QHBoxLayout;
78 hbox->setSpacing(6);
79 hbox->setContentsMargins(0, 0, 0, 0);
80
81 TextLabel3 = new QLabel(this);
82 TextLabel3->setText(i18n("To archive"));
83 hbox->addWidget(TextLabel3);
84
85 nameData = new QLineEdit(this);
86 hbox->addWidget(nameData);
87
88 typeData = new QComboBox(this);
89 typeData->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
90 connect(typeData, SIGNAL(activated(QString)), this, SLOT(checkConsistency()));
91 connect(typeData, SIGNAL(highlighted(QString)), this, SLOT(checkConsistency()));
92 hbox->addWidget(typeData);
93
94 grid->addLayout(hbox, 1, 0);
95
96 hbox_2 = new QHBoxLayout;
97 hbox_2->setSpacing(6);
98 hbox_2->setContentsMargins(0, 0, 0, 0);
99
100 TextLabel5 = new QLabel(this);
101 TextLabel5->setText(i18n("In folder"));
102 hbox_2->addWidget(TextLabel5);
103
104 dirData = new QLineEdit(this);
105 hbox_2->addWidget(dirData);
106
107 browseButton = new QToolButton(this);
108 browseButton->setIcon(Icon("document-open"));
109 hbox_2->addWidget(browseButton);
110 QSpacerItem* spacer = new QSpacerItem(48, 20, QSizePolicy::Fixed, QSizePolicy::Fixed);
111 hbox_2->addItem(spacer);
112
113 grid->addLayout(hbox_2, 2, 0);
114
115 hbox_3 = new QHBoxLayout;
116 hbox_3->setSpacing(6);
117 hbox_3->setContentsMargins(0, 0, 0, 0);
118
119 PixmapLabel1 = new QLabel(this);
120 PixmapLabel1->setPixmap(Icon("package-x-generic").pixmap(32));
121 PixmapLabel1->setScaledContents(true);
122 PixmapLabel1->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
123 hbox_3->addWidget(PixmapLabel1);
124
125 TextLabel1 = new QLabel(this);
126 TextLabel1->setText(i18n("Pack"));
127 hbox_3->addWidget(TextLabel1);
128
129 grid->addLayout(hbox_3, 0, 0);
130
131
132 hbox_4 = new QHBoxLayout;
133 hbox_4->setSpacing(6);
134 hbox_4->setContentsMargins(0, 0, 0, 0);
135
136 QSpacerItem* spacer_3 = new QSpacerItem(20, 26, QSizePolicy::Fixed, QSizePolicy::Expanding);
137 hbox_4->addItem(spacer_3);
138 grid->addLayout(hbox_4, 3, 0);
139
140 advancedWidget = new QWidget(this);
141
142 hbox_5 = new QGridLayout(advancedWidget);
143 hbox_5->setSpacing(6);
144 hbox_5->setContentsMargins(0, 0, 0, 0);
145
146
147 QVBoxLayout *compressLayout = new QVBoxLayout;
148 compressLayout->setSpacing(6);
149 compressLayout->setContentsMargins(0, 0, 0, 0);
150
151 multipleVolume = new QCheckBox(i18n("Multiple volume archive"), advancedWidget);
152 connect(multipleVolume, SIGNAL(toggled(bool)), this, SLOT(checkConsistency()));
153 compressLayout->addWidget(multipleVolume, 0, 0);
154
155 QHBoxLayout * volumeHbox = new QHBoxLayout;
156
157 QSpacerItem* spacer_5 = new QSpacerItem(20, 26, QSizePolicy::Fixed, QSizePolicy::Fixed);
158 volumeHbox->addItem(spacer_5);
159
160 TextLabel7 = new QLabel(i18n("Size:"), advancedWidget);
161 volumeHbox->addWidget(TextLabel7);
162
163 volumeSpinBox = new QSpinBox(advancedWidget);
164 volumeSpinBox->setMinimum(1);
165 volumeSpinBox->setMaximum(9999);
166 volumeSpinBox->setValue(1440);
167 volumeHbox->addWidget(volumeSpinBox);
168
169 volumeUnitCombo = new QComboBox(advancedWidget);
170 volumeUnitCombo->addItem("B");
171 volumeUnitCombo->addItem("KB");
172 volumeUnitCombo->addItem("MB");
173 volumeUnitCombo->setCurrentIndex(1);
174 volumeHbox->addWidget(volumeUnitCombo);
175
176 compressLayout->addLayout(volumeHbox);
177
178 int level = group.readEntry("Compression level", _defaultCompressionLevel);
179 setCompressionLevel = new QCheckBox(i18n("Set compression level"), advancedWidget);
180 if (level != _defaultCompressionLevel)
181 setCompressionLevel->setChecked(true);
182 connect(setCompressionLevel, SIGNAL(toggled(bool)), this, SLOT(checkConsistency()));
183 compressLayout->addWidget(setCompressionLevel, 0, 0);
184
185 QHBoxLayout * sliderHbox = new QHBoxLayout;
186
187 QSpacerItem* spacer_6 = new QSpacerItem(20, 26, QSizePolicy::Fixed, QSizePolicy::Fixed);
188 sliderHbox->addItem(spacer_6);
189
190 QWidget * sliderVBoxWidget = new QWidget(advancedWidget);
191 QVBoxLayout *sliderVBox = new QVBoxLayout(sliderVBoxWidget);
192
193 compressionSlider = new QSlider(Qt::Horizontal, sliderVBoxWidget);
194 compressionSlider->setMinimum(1);
195 compressionSlider->setMaximum(9);
196 compressionSlider->setPageStep(1);
197 compressionSlider->setValue(level);
198 compressionSlider->setTickPosition(QSlider::TicksBelow);
199 sliderVBox->addWidget(compressionSlider);
200
201 QWidget * minmaxWidget = new QWidget(sliderVBoxWidget);
202 sliderVBox->addWidget(minmaxWidget);
203
204 QHBoxLayout * minmaxHbox = new QHBoxLayout(minmaxWidget);
205
206 minLabel = new QLabel(i18n("MIN"), minmaxWidget);
207 maxLabel = new QLabel(i18n("MAX"), minmaxWidget);
208 maxLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
209
210 minmaxHbox->addWidget(minLabel);
211 minmaxHbox->addWidget(maxLabel);
212
213 sliderHbox->addWidget(sliderVBoxWidget);
214
215 compressLayout->addLayout(sliderHbox);
216
217 compressLayout->addStretch(0);
218 hbox_5->addLayout(compressLayout, 0, 0);
219
220 QFrame *vline = new QFrame(advancedWidget);
221 vline->setFrameStyle(QFrame::VLine | QFrame::Sunken);
222 vline->setMinimumWidth(20);
223 hbox_5->addWidget(vline, 0, 1);
224
225
226 QGridLayout * passwordGrid = new QGridLayout;
227 passwordGrid->setSpacing(6);
228 passwordGrid->setContentsMargins(0, 0, 0, 0);
229
230 TextLabel4 = new QLabel(advancedWidget);
231 TextLabel4->setText(i18n("Password"));
232 passwordGrid->addWidget(TextLabel4, 0, 0);
233
234 password = new QLineEdit(advancedWidget);
235 password->setEchoMode(QLineEdit::Password);
236 connect(password, SIGNAL(textChanged(QString)), this, SLOT(checkConsistency()));
237
238 passwordGrid->addWidget(password, 0, 1);
239
240 TextLabel6 = new QLabel(advancedWidget);
241 TextLabel6->setText(i18n("Again"));
242 passwordGrid->addWidget(TextLabel6, 1, 0);
243
244 passwordAgain = new QLineEdit(advancedWidget);
245 passwordAgain->setEchoMode(QLineEdit::Password);
246 connect(passwordAgain, SIGNAL(textChanged(QString)), this, SLOT(checkConsistency()));
247
248 passwordGrid->addWidget(passwordAgain, 1, 1);
249
250 QHBoxLayout *consistencyHbox = new QHBoxLayout;
251
252 QSpacerItem* spacer_cons = new QSpacerItem(48, 20, QSizePolicy::Expanding, QSizePolicy::Fixed);
253 consistencyHbox->addItem(spacer_cons);
254
255 passwordConsistencyLabel = new QLabel(advancedWidget);
256 consistencyHbox->addWidget(passwordConsistencyLabel);
257 passwordGrid->addLayout(consistencyHbox, 2, 0, 1, 2);
258
259 encryptHeaders = new QCheckBox(i18n("Encrypt headers"), advancedWidget);
260 passwordGrid->addWidget(encryptHeaders, 3, 0, 1, 2);
261
262 QSpacerItem* spacer_psw = new QSpacerItem(20, 20, QSizePolicy::Fixed, QSizePolicy::Expanding);
263 passwordGrid->addItem(spacer_psw, 4, 0);
264
265 hbox_5->addLayout(passwordGrid, 0, 2);
266
267 hbox_7 = new QHBoxLayout;
268 hbox_7->setSpacing(6);
269 hbox_7->setContentsMargins(0, 0, 0, 0);
270
271 TextLabel8 = new QLabel(i18n("Command line switches:"), advancedWidget);
272 TextLabel8->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
273 hbox_7->addWidget(TextLabel8);
274
275 commandLineSwitches = new KHistoryComboBox(advancedWidget);
276 commandLineSwitches->setMaxCount(25); // remember 25 items
277 commandLineSwitches->setDuplicatesEnabled(false);
278 commandLineSwitches->setMinimumContentsLength(10);
279
280 QStringList list = group.readEntry("Command Line Switches", QStringList());
281 commandLineSwitches->setHistoryItems(list);
282
283 hbox_7->addWidget(commandLineSwitches);
284
285 hbox_5->addLayout(hbox_7, 1, 0, 1, 3);
286
287
288 advancedWidget->hide();
289 checkConsistency();
290
291 grid->addWidget(advancedWidget, 4, 0);
292
293 hbox_6 = new QHBoxLayout;
294 hbox_6->setSpacing(6);
295 hbox_6->setContentsMargins(0, 0, 0, 0);
296
297 advancedButton = new QPushButton(this);
298 advancedButton->setText(i18n("&Advanced >>"));
299 hbox_6->addWidget(advancedButton);
300
301 QSpacerItem* spacer_2 = new QSpacerItem(140, 20, QSizePolicy::Expanding, QSizePolicy::Fixed);
302 hbox_6->addItem(spacer_2);
303
304 okButton = new QPushButton(this);
305 KStandardGuiItem::assign(okButton, KStandardGuiItem::Ok);
306 okButton->setDefault(true);
307 hbox_6->addWidget(okButton);
308
309 cancelButton = new QPushButton(this);
310 KStandardGuiItem::assign(cancelButton, KStandardGuiItem::Cancel);
311 hbox_6->addWidget(cancelButton);
312
313 grid->addLayout(hbox_6, 6, 0);
314
315 // signals and slots connections
316 connect(okButton, SIGNAL(clicked()), this, SLOT(accept()));
317 connect(advancedButton, SIGNAL(clicked()), this, SLOT(expand()));
318 connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
319 connect(browseButton, SIGNAL(clicked()), this, SLOT(browse()));
320 }
321
322 /*
323 * Destroys the object and frees any allocated resources
324 */
~PackGUIBase()325 PackGUIBase::~PackGUIBase()
326 {
327 // no need to delete child widgets, Qt does it all for us
328 }
329
browse()330 void PackGUIBase::browse()
331 {
332 qWarning("PackGUIBase::browse(): Not implemented yet!");
333 }
334
expand()335 void PackGUIBase::expand()
336 {
337 expanded = !expanded;
338
339 advancedButton->setText(expanded ? i18n("&Advanced <<") : i18n("&Advanced >>"));
340
341 if (expanded)
342 advancedWidget->show();
343 else {
344 advancedWidget->hide();
345 layout()->activate();
346 QSize minSize = minimumSize();
347 resize(width(), minSize.height());
348 }
349 show();
350 }
351
checkConsistency()352 void PackGUIBase::checkConsistency()
353 {
354 QPalette p = QGuiApplication::palette();
355 QPalette pal = passwordConsistencyLabel->palette();
356 if (password->text().isEmpty() && passwordAgain->text().isEmpty()) {
357 pal.setColor(passwordConsistencyLabel->foregroundRole(), p.color(QPalette::Active, QPalette::Text));
358 passwordConsistencyLabel->setText(i18n("No password specified"));
359 } else if (password->text() == passwordAgain->text()) {
360 pal.setColor(passwordConsistencyLabel->foregroundRole(), p.color(QPalette::Active, QPalette::Text));
361 passwordConsistencyLabel->setText(i18n("The passwords are equal"));
362 } else {
363 pal.setColor(passwordConsistencyLabel->foregroundRole(), Qt::red);
364 passwordConsistencyLabel->setText(i18n("The passwords are different"));
365 }
366 passwordConsistencyLabel->setPalette(pal);
367
368 QString packer = typeData->currentText();
369
370 bool passworded = false;
371 if (packer == "7z" || packer == "rar" || packer == "zip" || packer == "arj")
372 passworded = true;
373
374 passwordConsistencyLabel->setEnabled(passworded);
375 password->setEnabled(passworded);
376 passwordAgain->setEnabled(passworded);
377 TextLabel4->setEnabled(passworded);
378 TextLabel6->setEnabled(passworded);
379
380 encryptHeaders->setEnabled(packer == "rar");
381
382 multipleVolume->setEnabled(packer == "rar" || packer == "arj");
383 bool volumeEnabled = multipleVolume->isEnabled() && multipleVolume->isChecked();
384 volumeSpinBox->setEnabled(volumeEnabled);
385 volumeUnitCombo->setEnabled(volumeEnabled);
386 TextLabel7->setEnabled(volumeEnabled);
387
388 /* TODO */
389 setCompressionLevel->setEnabled(packer == "rar" || packer == "arj" || packer == "zip" ||
390 packer == "7z");
391 bool sliderEnabled = setCompressionLevel->isEnabled() && setCompressionLevel->isChecked();
392 compressionSlider->setEnabled(sliderEnabled);
393 minLabel->setEnabled(sliderEnabled);
394 maxLabel->setEnabled(sliderEnabled);
395 }
396
extraProperties(QMap<QString,QString> & inMap)397 bool PackGUIBase::extraProperties(QMap<QString, QString> & inMap)
398 {
399 inMap.clear();
400
401 KConfigGroup group(krConfig, "Archives");
402
403 if (password->isEnabled() && passwordAgain->isEnabled()) {
404 if (password->text() != passwordAgain->text()) {
405 KMessageBox::error(this, i18n("Cannot pack, the passwords are different."));
406 return false;
407 }
408
409 if (!password->text().isEmpty()) {
410 inMap[ "Password" ] = password->text();
411
412 if (encryptHeaders->isEnabled() && encryptHeaders->isChecked())
413 inMap[ "EncryptHeaders" ] = '1';
414 }
415 }
416
417 if (multipleVolume->isEnabled() && multipleVolume->isChecked()) {
418 KIO::filesize_t size = volumeSpinBox->value();
419
420 switch (volumeUnitCombo->currentIndex()) {
421 case 2:
422 size *= 1000;
423 #if __GNUC__ >= 7
424 [[gnu::fallthrough]];
425 #endif
426 case 1:
427 size *= 1000;
428 default:
429 break;
430 }
431
432 if (size < 10000) {
433 KMessageBox::error(this, i18n("Invalid volume size."));
434 return false;
435 }
436
437 QString sbuffer;
438 sbuffer.sprintf("%llu", size);
439
440 inMap[ "VolumeSize" ] = sbuffer;
441 }
442
443 if (setCompressionLevel->isEnabled() && setCompressionLevel->isChecked()) {
444 inMap[ "CompressionLevel" ] = QString("%1").arg(compressionSlider->value());
445 int level = compressionSlider->value();
446 group.writeEntry("Compression level", level);
447 }
448
449 QString cmdArgs = commandLineSwitches->currentText().trimmed();
450 if (!cmdArgs.isEmpty()) {
451 bool firstChar = true;
452 QChar quote = QChar::Null;
453
454 for (int i = 0; i < cmdArgs.length(); i++) {
455 QChar ch(cmdArgs[ i ]);
456 if (ch.isSpace())
457 continue;
458
459 if (ch == quote) {
460 quote = QChar::Null;
461 continue;
462 }
463
464 if (firstChar && ch != QLatin1Char('-')) {
465 KMessageBox::error(this, i18n("Invalid command line switch.\nA switch must start with '-'."));
466 return false;
467 }
468
469 firstChar = false;
470
471 if (quote == QLatin1Char('"'))
472 continue;
473 if (quote == QChar::Null && (ch == QLatin1Char('\'') || ch == QLatin1Char('"')))
474 quote = ch;
475 if (ch == QLatin1Char('\\')) {
476 if (i == cmdArgs.length() - 1) {
477 KMessageBox::error(this, i18n("Invalid command line switch.\nBackslashes cannot be the last character."));
478 return false;
479 }
480 i++;
481 }
482 }
483
484 if (quote != QChar::Null) {
485 KMessageBox::error(this, i18n("Invalid command line switch.\nUnclosed quotation mark."));
486 return false;
487 }
488
489 commandLineSwitches->addToHistory(cmdArgs);
490 QStringList list = commandLineSwitches->historyItems();
491 group.writeEntry("Command Line Switches", list);
492
493 inMap[ "CommandLineSwitches" ] = cmdArgs;
494 }
495 return true;
496 }
497