• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdeutils API Reference
  • KDE Home
  • Contact Us
 

ark

  • sources
  • kde-4.14
  • kdeutils
  • ark
  • part
part.cpp
Go to the documentation of this file.
1 /*
2  * ark -- archiver for the KDE project
3  *
4  * Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
5  * Copyright (C) 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
6  * Copyright (C) 2009-2012 Raphael Kubo da Costa <rakuco@FreeBSD.org>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program 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 this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  */
23 
24 #include "part.h"
25 #include "archivemodel.h"
26 #include "archiveview.h"
27 #include "arkviewer.h"
28 #include "dnddbusinterfaceadaptor.h"
29 #include "infopanel.h"
30 #include "jobtracker.h"
31 #include "kerfuffle/archive.h"
32 #include "kerfuffle/extractiondialog.h"
33 #include "kerfuffle/jobs.h"
34 #include "kerfuffle/settings.h"
35 
36 #include <KAboutData>
37 #include <KAction>
38 #include <KActionCollection>
39 #include <KApplication>
40 #include <KConfigGroup>
41 #include <KDebug>
42 #include <KFileDialog>
43 #include <KGuiItem>
44 #include <KIO/Job>
45 #include <KIO/NetAccess>
46 #include <KIcon>
47 #include <KInputDialog>
48 #include <KMessageBox>
49 #include <KPluginFactory>
50 #include <KRun>
51 #include <KSelectAction>
52 #include <KStandardDirs>
53 #include <KStandardGuiItem>
54 #include <KTempDir>
55 #include <KToggleAction>
56 
57 #include <QAction>
58 #include <QCursor>
59 #include <QHeaderView>
60 #include <QMenu>
61 #include <QMimeData>
62 #include <QMouseEvent>
63 #include <QScopedPointer>
64 #include <QSplitter>
65 #include <QTimer>
66 #include <QVBoxLayout>
67 #include <QWeakPointer>
68 #include <QtDBus/QtDBus>
69 
70 using namespace Kerfuffle;
71 
72 K_PLUGIN_FACTORY(Factory, registerPlugin<Ark::Part>();)
73 K_EXPORT_PLUGIN(Factory("ark"))
74 
75 namespace Ark
76 {
77 
78 static quint32 s_instanceCounter = 1;
79 
80 Part::Part(QWidget *parentWidget, QObject *parent, const QVariantList& args)
81  : KParts::ReadWritePart(parent),
82  m_splitter(0),
83  m_busy(false),
84  m_jobTracker(0)
85 {
86  Q_UNUSED(args)
87  setComponentData(Factory::componentData(), false);
88 
89  new DndExtractAdaptor(this);
90 
91  const QString pathName = QString(QLatin1String("/DndExtract/%1")).arg(s_instanceCounter++);
92  if (!QDBusConnection::sessionBus().registerObject(pathName, this)) {
93  kFatal() << "Could not register a D-Bus object for drag'n'drop";
94  }
95 
96  m_model = new ArchiveModel(pathName, this);
97 
98  m_splitter = new QSplitter(Qt::Horizontal, parentWidget);
99  setWidget(m_splitter);
100 
101  m_view = new ArchiveView;
102  m_infoPanel = new InfoPanel(m_model);
103 
104  m_splitter->addWidget(m_view);
105  m_splitter->addWidget(m_infoPanel);
106 
107  QList<int> splitterSizes = ArkSettings::splitterSizes();
108  if (splitterSizes.isEmpty()) {
109  splitterSizes.append(200);
110  splitterSizes.append(100);
111  }
112  m_splitter->setSizes(splitterSizes);
113 
114  setupView();
115  setupActions();
116 
117  connect(m_model, SIGNAL(loadingStarted()),
118  this, SLOT(slotLoadingStarted()));
119  connect(m_model, SIGNAL(loadingFinished(KJob*)),
120  this, SLOT(slotLoadingFinished(KJob*)));
121  connect(m_model, SIGNAL(droppedFiles(QStringList,QString)),
122  this, SLOT(slotAddFiles(QStringList,QString)));
123  connect(m_model, SIGNAL(error(QString,QString)),
124  this, SLOT(slotError(QString,QString)));
125 
126  connect(this, SIGNAL(busy()),
127  this, SLOT(setBusyGui()));
128  connect(this, SIGNAL(ready()),
129  this, SLOT(setReadyGui()));
130  connect(this, SIGNAL(completed()),
131  this, SLOT(setFileNameFromArchive()));
132 
133  m_statusBarExtension = new KParts::StatusBarExtension(this);
134 
135  setXMLFile(QLatin1String( "ark_part.rc" ));
136 }
137 
138 Part::~Part()
139 {
140  saveSplitterSizes();
141 
142  m_extractFilesAction->menu()->deleteLater();
143 }
144 
145 void Part::registerJob(KJob* job)
146 {
147  if (!m_jobTracker) {
148  m_jobTracker = new JobTracker(widget());
149  m_statusBarExtension->addStatusBarItem(m_jobTracker->widget(0), 0, true);
150  m_jobTracker->widget(job)->show();
151  }
152  m_jobTracker->registerJob(job);
153 
154  emit busy();
155  connect(job, SIGNAL(result(KJob*)), this, SIGNAL(ready()));
156 }
157 
158 // TODO: One should construct a KUrl out of localPath in order to be able to handle
159 // non-local destinations (ie. trash:/ or a remote location)
160 // See bugs #189322 and #204323.
161 void Part::extractSelectedFilesTo(const QString& localPath)
162 {
163  kDebug() << "Extract to " << localPath;
164  if (!m_model) {
165  return;
166  }
167 
168  if (m_view->selectionModel()->selectedRows().count() != 1) {
169  m_view->selectionModel()->setCurrentIndex(m_view->currentIndex(), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
170  }
171  if (m_view->selectionModel()->selectedRows().count() != 1) {
172  return;
173  }
174 
175  QVariant internalRoot;
176  kDebug() << "valid " << m_view->currentIndex().parent().isValid();
177  if (m_view->currentIndex().parent().isValid()) {
178  internalRoot = m_model->entryForIndex(m_view->currentIndex().parent()).value(InternalID);
179  }
180 
181  if (internalRoot.isNull()) {
182  //we have the special case valid parent, but the parent does not
183  //actually correspond to an item in the archive, but an automatically
184  //created folder. for now, we will just use the filename of the node
185  //instead, but for plugins that rely on a non-filename value as the
186  //InternalId, this WILL break things. TODO find a solution
187  internalRoot = m_model->entryForIndex(m_view->currentIndex().parent()).value(FileName);
188  }
189 
190  QList<QVariant> files = selectedFilesWithChildren();
191  if (files.isEmpty()) {
192  return;
193  }
194 
195  kDebug() << "selected files are " << files;
196  Kerfuffle::ExtractionOptions options;
197  options[QLatin1String( "PreservePaths" )] = true;
198  if (!internalRoot.isNull()) {
199  options[QLatin1String("RootNode")] = internalRoot;
200  }
201 
202  ExtractJob *job = m_model->extractFiles(files, localPath, options);
203  registerJob(job);
204 
205  connect(job, SIGNAL(result(KJob*)),
206  this, SLOT(slotExtractionDone(KJob*)));
207 
208  job->start();
209 }
210 
211 void Part::setupView()
212 {
213  m_view->setModel(m_model);
214 
215  m_view->setSortingEnabled(true);
216 
217  connect(m_view->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
218  this, SLOT(updateActions()));
219  connect(m_view->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
220  this, SLOT(selectionChanged()));
221 
222  //TODO: fix an actual eventhandler
223  connect(m_view, SIGNAL(itemTriggered(QModelIndex)),
224  this, SLOT(slotPreview(QModelIndex)));
225 
226  connect(m_model, SIGNAL(columnsInserted(QModelIndex,int,int)),
227  this, SLOT(adjustColumns()));
228 }
229 
230 void Part::setupActions()
231 {
232  KToggleAction *showInfoPanelAction = new KToggleAction(i18nc("@action:inmenu", "Show information panel"), this);
233  actionCollection()->addAction(QLatin1String( "show-infopanel" ), showInfoPanelAction);
234  showInfoPanelAction->setChecked(m_splitter->sizes().at(1) > 0);
235  connect(showInfoPanelAction, SIGNAL(triggered(bool)),
236  this, SLOT(slotToggleInfoPanel(bool)));
237 
238  m_saveAsAction = KStandardAction::saveAs(this, SLOT(slotSaveAs()), actionCollection());
239 
240  m_previewAction = actionCollection()->addAction(QLatin1String( "preview" ));
241  m_previewAction->setText(i18nc("to preview a file inside an archive", "Pre&view"));
242  m_previewAction->setIcon(KIcon( QLatin1String( "document-preview-archive" )));
243  m_previewAction->setStatusTip(i18n("Click to preview the selected file"));
244  m_previewAction->setShortcuts(QList<QKeySequence>() << Qt::Key_Return << Qt::Key_Space);
245  connect(m_previewAction, SIGNAL(triggered(bool)),
246  this, SLOT(slotPreview()));
247 
248  m_extractFilesAction = actionCollection()->addAction(QLatin1String( "extract" ));
249  m_extractFilesAction->setText(i18n("E&xtract"));
250  m_extractFilesAction->setIcon(KIcon( QLatin1String( "archive-extract" )));
251  m_extractFilesAction->setStatusTip(i18n("Click to open an extraction dialog, where you can choose to extract either all files or just the selected ones"));
252  m_extractFilesAction->setShortcut(QKeySequence( QLatin1String( "Ctrl+E" ) ));
253  connect(m_extractFilesAction, SIGNAL(triggered(bool)),
254  this, SLOT(slotExtractFiles()));
255 
256  m_addFilesAction = actionCollection()->addAction(QLatin1String( "add" ));
257  m_addFilesAction->setIcon(KIcon( QLatin1String( "archive-insert" )));
258  m_addFilesAction->setText(i18n("Add &File..."));
259  m_addFilesAction->setStatusTip(i18n("Click to add files to the archive"));
260  connect(m_addFilesAction, SIGNAL(triggered(bool)),
261  this, SLOT(slotAddFiles()));
262 
263  m_addDirAction = actionCollection()->addAction(QLatin1String( "add-dir" ));
264  m_addDirAction->setIcon(KIcon( QLatin1String( "archive-insert-directory" )));
265  m_addDirAction->setText(i18n("Add Fo&lder..."));
266  m_addDirAction->setStatusTip(i18n("Click to add a folder to the archive"));
267  connect(m_addDirAction, SIGNAL(triggered(bool)),
268  this, SLOT(slotAddDir()));
269 
270  m_deleteFilesAction = actionCollection()->addAction(QLatin1String( "delete" ));
271  m_deleteFilesAction->setIcon(KIcon( QLatin1String( "archive-remove" )));
272  m_deleteFilesAction->setText(i18n("De&lete"));
273  m_deleteFilesAction->setShortcut(Qt::Key_Delete);
274  m_deleteFilesAction->setStatusTip(i18n("Click to delete the selected files"));
275  connect(m_deleteFilesAction, SIGNAL(triggered(bool)),
276  this, SLOT(slotDeleteFiles()));
277 
278  updateActions();
279 }
280 
281 void Part::updateActions()
282 {
283  bool isWritable = m_model->archive() && (!m_model->archive()->isReadOnly());
284 
285  m_previewAction->setEnabled(!isBusy() && (m_view->selectionModel()->selectedRows().count() == 1)
286  && isPreviewable(m_view->selectionModel()->currentIndex()));
287  m_extractFilesAction->setEnabled(!isBusy() && (m_model->rowCount() > 0));
288  m_addFilesAction->setEnabled(!isBusy() && isWritable);
289  m_addDirAction->setEnabled(!isBusy() && isWritable);
290  m_deleteFilesAction->setEnabled(!isBusy() && (m_view->selectionModel()->selectedRows().count() > 0)
291  && isWritable);
292 
293  QMenu *menu = m_extractFilesAction->menu();
294  if (!menu) {
295  menu = new QMenu;
296  m_extractFilesAction->setMenu(menu);
297  connect(menu, SIGNAL(triggered(QAction*)),
298  this, SLOT(slotQuickExtractFiles(QAction*)));
299 
300  // Remember to keep this action's properties as similar to
301  // m_extractFilesAction's as possible (except where it does not make
302  // sense, such as the text or the shortcut).
303  QAction *extractTo = menu->addAction(i18n("Extract To..."));
304  extractTo->setIcon(m_extractFilesAction->icon());
305  extractTo->setStatusTip(m_extractFilesAction->statusTip());
306  connect(extractTo, SIGNAL(triggered(bool)), SLOT(slotExtractFiles()));
307 
308  menu->addSeparator();
309 
310  QAction *header = menu->addAction(i18n("Quick Extract To..."));
311  header->setEnabled(false);
312  header->setIcon(KIcon( QLatin1String( "archive-extract" )));
313  }
314 
315  while (menu->actions().size() > 3) {
316  menu->removeAction(menu->actions().last());
317  }
318 
319  const KConfigGroup conf(KGlobal::config(), "DirSelect Dialog");
320  const QStringList dirHistory = conf.readPathEntry("History Items", QStringList());
321 
322  for (int i = 0; i < qMin(10, dirHistory.size()); ++i) {
323  const KUrl dirUrl(dirHistory.at(i));
324  QAction *newAction = menu->addAction(dirUrl.pathOrUrl());
325  newAction->setData(dirUrl.pathOrUrl());
326  }
327 }
328 
329 void Part::slotQuickExtractFiles(QAction *triggeredAction)
330 {
331  // #190507: triggeredAction->data.isNull() means it's the "Extract to..."
332  // action, and we do not want it to run here
333  if (!triggeredAction->data().isNull()) {
334  kDebug() << "Extract to " << triggeredAction->data().toString();
335 
336  const QString userDestination = triggeredAction->data().toString();
337  QString finalDestinationDirectory;
338  const QString detectedSubfolder = detectSubfolder();
339 
340  if (!isSingleFolderArchive()) {
341  finalDestinationDirectory = userDestination +
342  QDir::separator() + detectedSubfolder;
343  QDir(userDestination).mkdir(detectedSubfolder);
344  } else {
345  finalDestinationDirectory = userDestination;
346  }
347 
348  Kerfuffle::ExtractionOptions options;
349  options[QLatin1String( "PreservePaths" )] = true;
350  QList<QVariant> files = selectedFiles();
351  ExtractJob *job = m_model->extractFiles(files, finalDestinationDirectory, options);
352  registerJob(job);
353 
354  connect(job, SIGNAL(result(KJob*)),
355  this, SLOT(slotExtractionDone(KJob*)));
356 
357  job->start();
358  }
359 }
360 
361 bool Part::isPreviewable(const QModelIndex& index) const
362 {
363  return index.isValid() && (!m_model->entryForIndex(index)[ IsDirectory ].toBool());
364 }
365 
366 void Part::selectionChanged()
367 {
368  m_infoPanel->setIndexes(m_view->selectionModel()->selectedRows());
369 }
370 
371 KAboutData* Part::createAboutData()
372 {
373  return new KAboutData("ark", 0, ki18n("ArkPart"), "3.0");
374 }
375 
376 bool Part::openFile()
377 {
378  const QString localFile(localFilePath());
379  const QFileInfo localFileInfo(localFile);
380  const bool creatingNewArchive =
381  arguments().metaData()[QLatin1String("createNewArchive")] == QLatin1String("true");
382 
383  if (localFileInfo.isDir()) {
384  KMessageBox::error(NULL, i18nc("@info",
385  "<filename>%1</filename> is a directory.",
386  localFile));
387  return false;
388  }
389 
390  if (creatingNewArchive) {
391  if (localFileInfo.exists()) {
392  int overwrite = KMessageBox::questionYesNo(NULL, i18nc("@info", "The archive <filename>%1</filename> already exists. Would you like to open it instead?", localFile), i18nc("@title:window", "File Exists"), KGuiItem(i18n("Open File")), KStandardGuiItem::cancel());
393 
394  if (overwrite == KMessageBox::No) {
395  return false;
396  }
397  }
398  } else {
399  if (!localFileInfo.exists()) {
400  KMessageBox::sorry(NULL, i18nc("@info", "The archive <filename>%1</filename> was not found.", localFile), i18nc("@title:window", "Error Opening Archive"));
401  return false;
402  }
403  }
404 
405  QScopedPointer<Kerfuffle::Archive> archive(Kerfuffle::Archive::create(localFile, m_model));
406 
407  if ((!archive) || ((creatingNewArchive) && (archive->isReadOnly()))) {
408  QStringList mimeTypeList;
409  QHash<QString, QString> mimeTypes;
410 
411  if (creatingNewArchive) {
412  mimeTypeList = Kerfuffle::supportedWriteMimeTypes();
413  } else {
414  mimeTypeList = Kerfuffle::supportedMimeTypes();
415  }
416 
417  foreach(const QString& mime, mimeTypeList) {
418  KMimeType::Ptr mimePtr(KMimeType::mimeType(mime));
419  if (mimePtr) {
420  // Key = "application/zip", Value = "Zip Archive"
421  mimeTypes[mime] = mimePtr->comment();
422  }
423  }
424 
425  QStringList mimeComments(mimeTypes.values());
426  mimeComments.sort();
427 
428  bool ok;
429  QString item;
430 
431  if (creatingNewArchive) {
432  item = KInputDialog::getItem(i18nc("@title:window", "Invalid Archive Type"),
433  i18nc("@info", "Ark cannot create archives of the type you have chosen.<nl/><nl/>Please choose another archive type below."),
434  mimeComments, 0, false, &ok);
435  } else {
436  item = KInputDialog::getItem(i18nc("@title:window", "Unable to Determine Archive Type"),
437  i18nc("@info", "Ark was unable to determine the archive type of the filename.<nl/><nl/>Please choose the correct archive type below."),
438  mimeComments,
439  0,
440  false,
441  &ok);
442  }
443 
444  if ((!ok) || (item.isEmpty())) {
445  return false;
446  }
447 
448  archive.reset(Kerfuffle::Archive::create(localFile, mimeTypes.key(item), m_model));
449  }
450 
451  if (!archive) {
452  KMessageBox::sorry(NULL, i18nc("@info", "Ark was not able to open the archive <filename>%1</filename>. No plugin capable of handling the file was found.", localFile), i18nc("@title:window", "Error Opening Archive"));
453  return false;
454  }
455 
456  KJob *job = m_model->setArchive(archive.take());
457  registerJob(job);
458  job->start();
459  m_infoPanel->setIndex(QModelIndex());
460 
461  if (arguments().metaData()[QLatin1String( "showExtractDialog" )] == QLatin1String( "true" )) {
462  QTimer::singleShot(0, this, SLOT(slotExtractFiles()));
463  }
464 
465  return true;
466 }
467 
468 bool Part::saveFile()
469 {
470  return true;
471 }
472 
473 bool Part::isBusy() const
474 {
475  return m_busy;
476 }
477 
478 void Part::slotLoadingStarted()
479 {
480 }
481 
482 void Part::slotLoadingFinished(KJob *job)
483 {
484  kDebug();
485 
486  if (job->error()) {
487  if (arguments().metaData()[QLatin1String( "createNewArchive" )] != QLatin1String( "true" )) {
488  KMessageBox::sorry(NULL, i18nc("@info", "Loading the archive <filename>%1</filename> failed with the following error: <message>%2</message>", localFilePath(), job->errorText()), i18nc("@title:window", "Error Opening Archive"));
489 
490  // The file failed to open, so reset the open archive, info panel and caption.
491  m_model->setArchive(NULL);
492 
493  m_infoPanel->setPrettyFileName(QString());
494  m_infoPanel->updateWithDefaults();
495 
496  emit setWindowCaption(QString());
497  }
498  }
499 
500  m_view->sortByColumn(0, Qt::AscendingOrder);
501  m_view->expandToDepth(0);
502 
503  // After loading all files, resize the columns to fit all fields
504  m_view->header()->resizeSections(QHeaderView::ResizeToContents);
505 
506  updateActions();
507 }
508 
509 void Part::setReadyGui()
510 {
511  kDebug();
512  QApplication::restoreOverrideCursor();
513  m_busy = false;
514  m_view->setEnabled(true);
515  updateActions();
516 }
517 
518 void Part::setBusyGui()
519 {
520  kDebug();
521  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
522  m_busy = true;
523  m_view->setEnabled(false);
524  updateActions();
525 }
526 
527 void Part::setFileNameFromArchive()
528 {
529  const QString prettyName = url().fileName();
530 
531  m_infoPanel->setPrettyFileName(prettyName);
532  m_infoPanel->updateWithDefaults();
533 
534  emit setWindowCaption(prettyName);
535 }
536 
537 void Part::slotPreview()
538 {
539  slotPreview(m_view->selectionModel()->currentIndex());
540 }
541 
542 void Part::slotPreview(const QModelIndex & index)
543 {
544  if (!isPreviewable(index)) {
545  return;
546  }
547 
548  const ArchiveEntry& entry = m_model->entryForIndex(index);
549 
550  if (!entry.isEmpty()) {
551  Kerfuffle::ExtractionOptions options;
552  options[QLatin1String( "PreservePaths" )] = true;
553 
554  ExtractJob *job = m_model->extractFile(entry[ InternalID ], m_previewDir.name(), options);
555  registerJob(job);
556  connect(job, SIGNAL(result(KJob*)),
557  this, SLOT(slotPreviewExtracted(KJob*)));
558  job->start();
559  }
560 }
561 
562 void Part::slotPreviewExtracted(KJob *job)
563 {
564  // FIXME: the error checking here isn't really working
565  // if there's an error or an overwrite dialog,
566  // the preview dialog will be launched anyway
567  if (!job->error()) {
568  const ArchiveEntry& entry =
569  m_model->entryForIndex(m_view->selectionModel()->currentIndex());
570 
571  QString fullName =
572  m_previewDir.name() + QLatin1Char('/') + entry[FileName].toString();
573 
574  // Make sure a maliciously crafted archive with parent folders named ".." do
575  // not cause the previewed file path to be located outside the temporary
576  // directory, resulting in a directory traversal issue.
577  fullName.remove(QLatin1String("../"));
578 
579  ArkViewer::view(fullName, widget());
580  } else {
581  KMessageBox::error(widget(), job->errorString());
582  }
583  setReadyGui();
584 }
585 
586 void Part::slotError(const QString& errorMessage, const QString& details)
587 {
588  if (details.isEmpty()) {
589  KMessageBox::error(widget(), errorMessage);
590  } else {
591  KMessageBox::detailedError(widget(), errorMessage, details);
592  }
593 }
594 
595 bool Part::isSingleFolderArchive() const
596 {
597  return m_model->archive()->isSingleFolderArchive();
598 }
599 
600 QString Part::detectSubfolder() const
601 {
602  if (!m_model) {
603  return QString();
604  }
605 
606  return m_model->archive()->subfolderName();
607 }
608 
609 void Part::slotExtractFiles()
610 {
611  if (!m_model) {
612  return;
613  }
614 
615  QWeakPointer<Kerfuffle::ExtractionDialog> dialog = new Kerfuffle::ExtractionDialog;
616 
617  if (m_view->selectionModel()->selectedRows().count() > 0) {
618  dialog.data()->setShowSelectedFiles(true);
619  }
620 
621  dialog.data()->setSingleFolderArchive(isSingleFolderArchive());
622  dialog.data()->setSubfolder(detectSubfolder());
623 
624  dialog.data()->setCurrentUrl(QFileInfo(m_model->archive()->fileName()).path());
625 
626  if (dialog.data()->exec()) {
627  //this is done to update the quick extract menu
628  updateActions();
629 
630  QVariantList files;
631 
632  //if the user has chosen to extract only selected entries, fetch these
633  //from the listview
634  if (!dialog.data()->extractAllFiles()) {
635  files = selectedFilesWithChildren();
636  }
637 
638  kDebug() << "Selected " << files;
639 
640  Kerfuffle::ExtractionOptions options;
641 
642  if (dialog.data()->preservePaths()) {
643  options[QLatin1String("PreservePaths")] = true;
644  }
645 
646  options[QLatin1String("FollowExtractionDialogSettings")] = true;
647 
648  const QString destinationDirectory = dialog.data()->destinationDirectory().pathOrUrl();
649  ExtractJob *job = m_model->extractFiles(files, destinationDirectory, options);
650  registerJob(job);
651 
652  connect(job, SIGNAL(result(KJob*)),
653  this, SLOT(slotExtractionDone(KJob*)));
654 
655  job->start();
656  }
657 
658  delete dialog.data();
659 }
660 
661 QList<QVariant> Part::selectedFilesWithChildren()
662 {
663  Q_ASSERT(m_model);
664 
665  QModelIndexList toIterate = m_view->selectionModel()->selectedRows();
666 
667  for (int i = 0; i < toIterate.size(); ++i) {
668  QModelIndex index = toIterate.at(i);
669 
670  for (int j = 0; j < m_model->rowCount(index); ++j) {
671  QModelIndex child = m_model->index(j, 0, index);
672  if (!toIterate.contains(child)) {
673  toIterate << child;
674  }
675  }
676  }
677 
678  QVariantList ret;
679  foreach(const QModelIndex & index, toIterate) {
680  const ArchiveEntry& entry = m_model->entryForIndex(index);
681  if (entry.contains(InternalID)) {
682  ret << entry[ InternalID ];
683  }
684  }
685  return ret;
686 }
687 
688 QList<QVariant> Part::selectedFiles()
689 {
690  QStringList toSort;
691 
692  foreach(const QModelIndex & index, m_view->selectionModel()->selectedRows()) {
693  const ArchiveEntry& entry = m_model->entryForIndex(index);
694  toSort << entry[ InternalID ].toString();
695  }
696 
697  toSort.sort();
698  QVariantList ret;
699  foreach(const QString &i, toSort) {
700  ret << i;
701  }
702  return ret;
703 }
704 
705 void Part::slotExtractionDone(KJob* job)
706 {
707  kDebug();
708  if (job->error()) {
709  KMessageBox::error(widget(), job->errorString());
710  } else {
711  ExtractJob *extractJob = qobject_cast<ExtractJob*>(job);
712  Q_ASSERT(extractJob);
713 
714  const bool followExtractionDialogSettings =
715  extractJob->extractionOptions().value(QLatin1String("FollowExtractionDialogSettings"), false).toBool();
716  if (!followExtractionDialogSettings) {
717  return;
718  }
719 
720  if (ArkSettings::openDestinationFolderAfterExtraction()) {
721 
722  KUrl destinationDirectory(extractJob->destinationDirectory());
723  destinationDirectory.cleanPath();
724 
725  KRun::runUrl(destinationDirectory, QLatin1String("inode/directory"), widget());
726  }
727 
728  if (ArkSettings::closeAfterExtraction()) {
729  emit quit();
730  }
731  }
732 }
733 
734 void Part::adjustColumns()
735 {
736  kDebug();
737 
738  m_view->header()->setResizeMode(0, QHeaderView::ResizeToContents);
739 }
740 
741 void Part::slotAddFiles(const QStringList& filesToAdd, const QString& path)
742 {
743  if (filesToAdd.isEmpty()) {
744  return;
745  }
746 
747  kDebug() << "Adding " << filesToAdd << " to " << path;
748  kDebug() << "Warning, for now the path argument is not implemented";
749 
750  QStringList cleanFilesToAdd(filesToAdd);
751  for (int i = 0; i < cleanFilesToAdd.size(); ++i) {
752  QString& file = cleanFilesToAdd[i];
753  if (QFileInfo(file).isDir()) {
754  if (!file.endsWith(QLatin1Char( '/' ))) {
755  file += QLatin1Char( '/' );
756  }
757  }
758  }
759 
760  CompressionOptions options;
761 
762  QString firstPath = cleanFilesToAdd.first();
763  if (firstPath.right(1) == QLatin1String( "/" )) {
764  firstPath.chop(1);
765  }
766  firstPath = QFileInfo(firstPath).dir().absolutePath();
767 
768  kDebug() << "Detected relative path to be " << firstPath;
769  options[QLatin1String( "GlobalWorkDir" )] = firstPath;
770 
771  AddJob *job = m_model->addFiles(cleanFilesToAdd, options);
772  if (!job) {
773  return;
774  }
775 
776  connect(job, SIGNAL(result(KJob*)),
777  this, SLOT(slotAddFilesDone(KJob*)));
778  registerJob(job);
779  job->start();
780 }
781 
782 void Part::slotAddFiles()
783 {
784  kDebug();
785 
786  // #264819: passing widget() as the parent will not work as expected.
787  // KFileDialog will create a KFileWidget, which runs an internal
788  // event loop to stat the given directory. This, in turn, leads to
789  // events being delivered to widget(), which is a QSplitter, which
790  // in turn reimplements childEvent() and will end up calling
791  // QWidget::show() on the KFileDialog (thus showing it in a
792  // non-modal state).
793  // When KFileDialog::exec() is called, the widget is already shown
794  // and nothing happens.
795  const QStringList filesToAdd =
796  KFileDialog::getOpenFileNames(KUrl("kfiledialog:///ArkAddFiles"),
797  QString(), widget()->parentWidget(),
798  i18nc("@title:window", "Add Files"));
799 
800  slotAddFiles(filesToAdd);
801 }
802 
803 void Part::slotAddDir()
804 {
805  kDebug();
806  const QString dirToAdd = KFileDialog::getExistingDirectory(KUrl("kfiledialog:///ArkAddFiles"), widget(), i18nc("@title:window", "Add Folder"));
807 
808  if (!dirToAdd.isEmpty()) {
809  slotAddFiles(QStringList() << dirToAdd);
810  }
811 }
812 
813 void Part::slotAddFilesDone(KJob* job)
814 {
815  kDebug();
816  if (job->error()) {
817  KMessageBox::error(widget(), job->errorString());
818  }
819 }
820 
821 void Part::slotDeleteFilesDone(KJob* job)
822 {
823  kDebug();
824  if (job->error()) {
825  KMessageBox::error(widget(), job->errorString());
826  }
827 }
828 
829 void Part::slotDeleteFiles()
830 {
831  kDebug();
832 
833  const int reallyDelete =
834  KMessageBox::questionYesNo(NULL,
835  i18n("Deleting these files is not undoable. Are you sure you want to do this?"),
836  i18nc("@title:window", "Delete files"),
837  KStandardGuiItem::del(),
838  KStandardGuiItem::cancel(),
839  QString(),
840  KMessageBox::Dangerous | KMessageBox::Notify);
841 
842  if (reallyDelete == KMessageBox::No) {
843  return;
844  }
845 
846  DeleteJob *job = m_model->deleteFiles(selectedFilesWithChildren());
847  connect(job, SIGNAL(result(KJob*)),
848  this, SLOT(slotDeleteFilesDone(KJob*)));
849  registerJob(job);
850  job->start();
851 }
852 
853 void Part::slotToggleInfoPanel(bool visible)
854 {
855  QList<int> splitterSizes;
856 
857  if (visible) {
858  splitterSizes = ArkSettings::splitterSizesWithBothWidgets();
859  } else {
860  splitterSizes = m_splitter->sizes();
861  ArkSettings::setSplitterSizesWithBothWidgets(splitterSizes);
862  splitterSizes[1] = 0;
863  }
864 
865  m_splitter->setSizes(splitterSizes);
866  saveSplitterSizes();
867 }
868 
869 void Part::saveSplitterSizes()
870 {
871  ArkSettings::setSplitterSizes(m_splitter->sizes());
872  ArkSettings::self()->writeConfig();
873 }
874 
875 void Part::slotSaveAs()
876 {
877  KUrl saveUrl = KFileDialog::getSaveUrl(KUrl(QLatin1String( "kfiledialog:///ArkSaveAs/" ) + url().fileName()), QString(), widget());
878 
879  if ((saveUrl.isValid()) && (!saveUrl.isEmpty())) {
880  if (KIO::NetAccess::exists(saveUrl, KIO::NetAccess::DestinationSide, widget())) {
881  int overwrite = KMessageBox::warningContinueCancel(widget(),
882  i18nc("@info", "An archive named <filename>%1</filename> already exists. Are you sure you want to overwrite it?", saveUrl.fileName()),
883  QString(),
884  KStandardGuiItem::overwrite());
885 
886  if (overwrite != KMessageBox::Continue) {
887  return;
888  }
889  }
890 
891  KUrl srcUrl = KUrl::fromPath(localFilePath());
892 
893  if (!QFile::exists(localFilePath())) {
894  if (url().isLocalFile()) {
895  KMessageBox::error(widget(),
896  i18nc("@info", "The archive <filename>%1</filename> cannot be copied to the specified location. The archive does not exist anymore.", localFilePath()));
897 
898  return;
899  } else {
900  srcUrl = url();
901  }
902  }
903 
904  KIO::Job *copyJob = KIO::file_copy(srcUrl, saveUrl, -1, KIO::Overwrite);
905 
906  if (!KIO::NetAccess::synchronousRun(copyJob, widget())) {
907  KMessageBox::error(widget(),
908  i18nc("@info", "The archive could not be saved as <filename>%1</filename>. Try saving it to another location.", saveUrl.pathOrUrl()));
909  }
910  }
911 }
912 
913 } // namespace Ark
QHeaderView::resizeSections
void resizeSections(QHeaderView::ResizeMode mode)
ArchiveModel::extractFile
Kerfuffle::ExtractJob * extractFile(const QVariant &fileName, const QString &destinationDir, const Kerfuffle::ExtractionOptions options=Kerfuffle::ExtractionOptions()) const
Definition: archivemodel.cpp:895
QModelIndex
QWidget
ArkViewer::view
static void view(const QString &fileName, QWidget *parent=0)
Definition: arkviewer.cpp:89
Ark::Part::isBusy
bool isBusy() const
Definition: part.cpp:473
Kerfuffle::Job::start
void start()
Definition: jobs.cpp:103
QSplitter::setSizes
void setSizes(const QList< int > &list)
archivemodel.h
QHash::key
const Key key(const T &value) const
QItemSelectionModel::currentIndex
QModelIndex currentIndex() const
archiveview.h
Kerfuffle::IsDirectory
The entry is a directory.
Definition: archive.h:72
QAbstractItemView::selectionModel
QItemSelectionModel * selectionModel() const
Kerfuffle::supportedWriteMimeTypes
QStringList supportedWriteMimeTypes()
Definition: archive.cpp:306
Kerfuffle::DeleteJob
Definition: jobs.h:155
Kerfuffle::Archive::subfolderName
QString subfolderName()
Definition: archive.cpp:268
ArkSettings::openDestinationFolderAfterExtraction
static bool openDestinationFolderAfterExtraction()
Get Open destination folder after extraction.
Definition: settings.h:32
QAction::data
QVariant data() const
QAction::setIcon
void setIcon(const QIcon &icon)
QList::at
const T & at(int i) const
InfoPanel::setPrettyFileName
void setPrettyFileName(const QString &fileName)
Sets a different file name for the current open archive.
Definition: infopanel.cpp:90
QMenu::addAction
void addAction(QAction *action)
Kerfuffle::AddJob
Definition: jobs.h:140
InfoPanel::setIndex
void setIndex(const QModelIndex &)
Definition: infopanel.cpp:95
Kerfuffle::ExtractionDialog
Definition: extractiondialog.h:40
QWeakPointer::data
T * data() const
QDBusConnection::sessionBus
QDBusConnection sessionBus()
archive.h
QTreeView::sortByColumn
void sortByColumn(int column, Qt::SortOrder order)
QFile::exists
bool exists() const
QString::remove
QString & remove(int position, int n)
arkviewer.h
ArchiveModel::index
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
Definition: archivemodel.cpp:425
QString::chop
void chop(int n)
QDir::separator
QChar separator()
ArchiveModel::addFiles
Kerfuffle::AddJob * addFiles(const QStringList &paths, const Kerfuffle::CompressionOptions &options=Kerfuffle::CompressionOptions())
Definition: archivemodel.cpp:911
Kerfuffle::Archive::create
KJob * create()
Definition: archive.cpp:145
QList::size
int size() const
QSplitter::addWidget
void addWidget(QWidget *widget)
ArchiveModel
Definition: archivemodel.h:41
QScopedPointer::reset
void reset(T *other)
Ark::Part::extractSelectedFilesTo
void extractSelectedFilesTo(const QString &localPath)
Definition: part.cpp:161
Ark::Part::quit
void quit()
QModelIndex::isValid
bool isValid() const
Ark::Part::createAboutData
static KAboutData * createAboutData()
Definition: part.cpp:371
QWidget::setEnabled
void setEnabled(bool)
Kerfuffle::ArchiveEntry
QHash< int, QVariant > ArchiveEntry
Definition: archive.h:78
InfoPanel::updateWithDefaults
void updateWithDefaults()
Definition: infopanel.cpp:61
QList::append
void append(const T &value)
Kerfuffle::supportedMimeTypes
QStringList supportedMimeTypes()
Definition: archive.cpp:279
QSplitter::sizes
QList< int > sizes() const
QVariant::isNull
bool isNull() const
Kerfuffle::Archive::isSingleFolderArchive
bool isSingleFolderArchive()
Definition: archive.cpp:256
QHash
QFileInfo::isDir
bool isDir() const
QObject
jobtracker.h
QScopedPointer< Kerfuffle::Archive >
ArkSettings::self
static ArkSettings * self()
Definition: settings.cpp:19
QList::isEmpty
bool isEmpty() const
QString::isEmpty
bool isEmpty() const
QItemSelectionModel::selectedRows
QModelIndexList selectedRows(int column) const
ArchiveModel::setArchive
KJob * setArchive(Kerfuffle::Archive *archive)
Definition: archivemodel.cpp:863
QApplication::setOverrideCursor
void setOverrideCursor(const QCursor &cursor)
QApplication::restoreOverrideCursor
void restoreOverrideCursor()
QString::endsWith
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
K_PLUGIN_FACTORY
K_PLUGIN_FACTORY(ExtractHerePluginFactory, registerPlugin< ExtractHereDndPlugin >();) void ExtractHereDndPlugin
Definition: extractHereDndPlugin.cpp:33
QFileInfo::dir
QDir dir() const
QMenu::addSeparator
QAction * addSeparator()
QString
QList< int >
InfoPanel
Definition: infopanel.h:31
Ark::Part::ready
void ready()
QModelIndex::parent
QModelIndex parent() const
Kerfuffle::ExtractJob::extractionOptions
ExtractionOptions extractionOptions() const
Definition: jobs.cpp:292
ArchiveModel::archive
Kerfuffle::Archive * archive() const
Definition: archivemodel.cpp:858
QStringList
Kerfuffle::Archive::fileName
QString fileName() const
Definition: archive.cpp:194
QString::right
QString right(int n) const
Kerfuffle::FileName
The entry's file name.
Definition: archive.h:59
extractiondialog.h
ArchiveModel::deleteFiles
Kerfuffle::DeleteJob * deleteFiles(const QList< QVariant > &files)
Definition: archivemodel.cpp:930
QFileInfo
QHash::value
const T value(const Key &key) const
QFileInfo::exists
bool exists() const
QMenu
ArkSettings::setSplitterSizes
static void setSplitterSizes(const QList< int > &v)
Set splitterSizes.
Definition: settings.h:79
QLatin1Char
QTreeView::expandToDepth
void expandToDepth(int depth)
Kerfuffle::ExtractJob::destinationDirectory
QString destinationDirectory() const
Definition: jobs.cpp:287
Kerfuffle::ExtractJob
Definition: jobs.h:118
QDir
Ark::Part::saveFile
virtual bool saveFile()
Definition: part.cpp:468
ArkSettings::setSplitterSizesWithBothWidgets
static void setSplitterSizesWithBothWidgets(const QList< int > &v)
Set splitterSizesWithBothWidgets.
Definition: settings.h:98
QItemSelection
QSplitter
jobs.h
QTreeView::setSortingEnabled
void setSortingEnabled(bool enable)
QAction::setStatusTip
void setStatusTip(const QString &statusTip)
settings.h
QLatin1String
QKeySequence
QDir::absolutePath
QString absolutePath() const
ArchiveModel::rowCount
int rowCount(const QModelIndex &parent=QModelIndex()) const
Definition: archivemodel.cpp:485
QDir::mkdir
bool mkdir(const QString &dirName) const
Ark::Part::openFile
virtual bool openFile()
Definition: part.cpp:376
QTreeView::setModel
virtual void setModel(QAbstractItemModel *model)
QHeaderView::setResizeMode
void setResizeMode(ResizeMode mode)
QAction
QWidget::removeAction
void removeAction(QAction *action)
QHash::values
QList< T > values() const
QWeakPointer
InfoPanel::setIndexes
void setIndexes(const QModelIndexList &list)
Definition: infopanel.cpp:136
QStringList::sort
void sort()
ArkSettings::splitterSizes
static QList< int > splitterSizes()
Get splitterSizes.
Definition: settings.h:89
QItemSelectionModel::setCurrentIndex
void setCurrentIndex(const QModelIndex &index, QFlags< QItemSelectionModel::SelectionFlag > command)
QTreeView::header
QHeaderView * header() const
ArchiveModel::extractFiles
Kerfuffle::ExtractJob * extractFiles(const QList< QVariant > &files, const QString &destinationDir, const Kerfuffle::ExtractionOptions options=Kerfuffle::ExtractionOptions()) const
Definition: archivemodel.cpp:902
ArchiveView
Definition: archiveview.h:27
Kerfuffle::InternalID
The entry's ID for Ark's internal manipulation.
Definition: archive.h:60
JobTracker
Definition: jobtracker.h:39
QAbstractItemView::currentIndex
QModelIndex currentIndex() const
Ark::s_instanceCounter
static quint32 s_instanceCounter
Definition: part.cpp:78
QWidget::actions
QList< QAction * > actions() const
QScopedPointer::take
T * take()
QCursor
ArkSettings::splitterSizesWithBothWidgets
static QList< int > splitterSizesWithBothWidgets()
Get splitterSizesWithBothWidgets.
Definition: settings.h:108
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QVariant::toString
QString toString() const
Ark::Part::busy
void busy()
Ark::Part::~Part
~Part()
Definition: part.cpp:138
KJob
infopanel.h
QAction::setEnabled
void setEnabled(bool)
part.h
ArkSettings::closeAfterExtraction
static bool closeAfterExtraction()
Get Close Ark after extraction.
Definition: settings.h:51
Kerfuffle::Archive::isReadOnly
bool isReadOnly() const
Definition: archive.cpp:135
ArchiveModel::entryForIndex
Kerfuffle::ArchiveEntry entryForIndex(const QModelIndex &index)
Definition: archivemodel.cpp:453
QTimer::singleShot
singleShot
QVariant
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:42:37 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

ark

Skip menu "ark"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdeutils API Reference

Skip menu "kdeutils API Reference"
  • ark
  • filelight
  • kcalc
  • kcharselect
  • kdf
  • kfloppy
  • kgpg
  • ktimer
  • kwallet
  • sweeper

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal