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

ark

  • sources
  • kde-4.12
  • 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  connect(m_previewAction, SIGNAL(triggered(bool)),
245  this, SLOT(slotPreview()));
246 
247  m_extractFilesAction = actionCollection()->addAction(QLatin1String( "extract" ));
248  m_extractFilesAction->setText(i18n("E&xtract"));
249  m_extractFilesAction->setIcon(KIcon( QLatin1String( "archive-extract" )));
250  m_extractFilesAction->setStatusTip(i18n("Click to open an extraction dialog, where you can choose to extract either all files or just the selected ones"));
251  m_extractFilesAction->setShortcut(QKeySequence( QLatin1String( "Ctrl+E" ) ));
252  connect(m_extractFilesAction, SIGNAL(triggered(bool)),
253  this, SLOT(slotExtractFiles()));
254 
255  m_addFilesAction = actionCollection()->addAction(QLatin1String( "add" ));
256  m_addFilesAction->setIcon(KIcon( QLatin1String( "archive-insert" )));
257  m_addFilesAction->setText(i18n("Add &File..."));
258  m_addFilesAction->setStatusTip(i18n("Click to add files to the archive"));
259  connect(m_addFilesAction, SIGNAL(triggered(bool)),
260  this, SLOT(slotAddFiles()));
261 
262  m_addDirAction = actionCollection()->addAction(QLatin1String( "add-dir" ));
263  m_addDirAction->setIcon(KIcon( QLatin1String( "archive-insert-directory" )));
264  m_addDirAction->setText(i18n("Add Fo&lder..."));
265  m_addDirAction->setStatusTip(i18n("Click to add a folder to the archive"));
266  connect(m_addDirAction, SIGNAL(triggered(bool)),
267  this, SLOT(slotAddDir()));
268 
269  m_deleteFilesAction = actionCollection()->addAction(QLatin1String( "delete" ));
270  m_deleteFilesAction->setIcon(KIcon( QLatin1String( "archive-remove" )));
271  m_deleteFilesAction->setText(i18n("De&lete"));
272  m_deleteFilesAction->setShortcut(Qt::Key_Delete);
273  m_deleteFilesAction->setStatusTip(i18n("Click to delete the selected files"));
274  connect(m_deleteFilesAction, SIGNAL(triggered(bool)),
275  this, SLOT(slotDeleteFiles()));
276 
277  updateActions();
278 }
279 
280 void Part::updateActions()
281 {
282  bool isWritable = m_model->archive() && (!m_model->archive()->isReadOnly());
283 
284  m_previewAction->setEnabled(!isBusy() && (m_view->selectionModel()->selectedRows().count() == 1)
285  && isPreviewable(m_view->selectionModel()->currentIndex()));
286  m_extractFilesAction->setEnabled(!isBusy() && (m_model->rowCount() > 0));
287  m_addFilesAction->setEnabled(!isBusy() && isWritable);
288  m_addDirAction->setEnabled(!isBusy() && isWritable);
289  m_deleteFilesAction->setEnabled(!isBusy() && (m_view->selectionModel()->selectedRows().count() > 0)
290  && isWritable);
291 
292  QMenu *menu = m_extractFilesAction->menu();
293  if (!menu) {
294  menu = new QMenu;
295  m_extractFilesAction->setMenu(menu);
296  connect(menu, SIGNAL(triggered(QAction*)),
297  this, SLOT(slotQuickExtractFiles(QAction*)));
298 
299  // Remember to keep this action's properties as similar to
300  // m_extractFilesAction's as possible (except where it does not make
301  // sense, such as the text or the shortcut).
302  QAction *extractTo = menu->addAction(i18n("Extract To..."));
303  extractTo->setIcon(m_extractFilesAction->icon());
304  extractTo->setStatusTip(m_extractFilesAction->statusTip());
305  connect(extractTo, SIGNAL(triggered(bool)), SLOT(slotExtractFiles()));
306 
307  menu->addSeparator();
308 
309  QAction *header = menu->addAction(i18n("Quick Extract To..."));
310  header->setEnabled(false);
311  header->setIcon(KIcon( QLatin1String( "archive-extract" )));
312  }
313 
314  while (menu->actions().size() > 3) {
315  menu->removeAction(menu->actions().last());
316  }
317 
318  const KConfigGroup conf(KGlobal::config(), "DirSelect Dialog");
319  const QStringList dirHistory = conf.readPathEntry("History Items", QStringList());
320 
321  for (int i = 0; i < qMin(10, dirHistory.size()); ++i) {
322  const KUrl dirUrl(dirHistory.at(i));
323  QAction *newAction = menu->addAction(dirUrl.pathOrUrl());
324  newAction->setData(dirUrl.pathOrUrl());
325  }
326 }
327 
328 void Part::slotQuickExtractFiles(QAction *triggeredAction)
329 {
330  // #190507: triggeredAction->data.isNull() means it's the "Extract to..."
331  // action, and we do not want it to run here
332  if (!triggeredAction->data().isNull()) {
333  kDebug() << "Extract to " << triggeredAction->data().toString();
334 
335  const QString userDestination = triggeredAction->data().toString();
336  QString finalDestinationDirectory;
337  const QString detectedSubfolder = detectSubfolder();
338 
339  if (!isSingleFolderArchive()) {
340  finalDestinationDirectory = userDestination +
341  QDir::separator() + detectedSubfolder;
342  QDir(userDestination).mkdir(detectedSubfolder);
343  } else {
344  finalDestinationDirectory = userDestination;
345  }
346 
347  Kerfuffle::ExtractionOptions options;
348  options[QLatin1String( "PreservePaths" )] = true;
349  QList<QVariant> files = selectedFiles();
350  ExtractJob *job = m_model->extractFiles(files, finalDestinationDirectory, options);
351  registerJob(job);
352 
353  connect(job, SIGNAL(result(KJob*)),
354  this, SLOT(slotExtractionDone(KJob*)));
355 
356  job->start();
357  }
358 }
359 
360 bool Part::isPreviewable(const QModelIndex& index) const
361 {
362  return index.isValid() && (!m_model->entryForIndex(index)[ IsDirectory ].toBool());
363 }
364 
365 void Part::selectionChanged()
366 {
367  m_infoPanel->setIndexes(m_view->selectionModel()->selectedRows());
368 }
369 
370 KAboutData* Part::createAboutData()
371 {
372  return new KAboutData("ark", 0, ki18n("ArkPart"), "3.0");
373 }
374 
375 bool Part::openFile()
376 {
377  const QString localFile(localFilePath());
378  const QFileInfo localFileInfo(localFile);
379  const bool creatingNewArchive =
380  arguments().metaData()[QLatin1String("createNewArchive")] == QLatin1String("true");
381 
382  if (localFileInfo.isDir()) {
383  KMessageBox::error(NULL, i18nc("@info",
384  "<filename>%1</filename> is a directory.",
385  localFile));
386  return false;
387  }
388 
389  if (creatingNewArchive) {
390  if (localFileInfo.exists()) {
391  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());
392 
393  if (overwrite == KMessageBox::No) {
394  return false;
395  }
396  }
397  } else {
398  if (!localFileInfo.exists()) {
399  KMessageBox::sorry(NULL, i18nc("@info", "The archive <filename>%1</filename> was not found.", localFile), i18nc("@title:window", "Error Opening Archive"));
400  return false;
401  }
402  }
403 
404  QScopedPointer<Kerfuffle::Archive> archive(Kerfuffle::Archive::create(localFile, m_model));
405 
406  if ((!archive) || ((creatingNewArchive) && (archive->isReadOnly()))) {
407  QStringList mimeTypeList;
408  QHash<QString, QString> mimeTypes;
409 
410  if (creatingNewArchive) {
411  mimeTypeList = Kerfuffle::supportedWriteMimeTypes();
412  } else {
413  mimeTypeList = Kerfuffle::supportedMimeTypes();
414  }
415 
416  foreach(const QString& mime, mimeTypeList) {
417  KMimeType::Ptr mimePtr(KMimeType::mimeType(mime));
418  if (mimePtr) {
419  // Key = "application/zip", Value = "Zip Archive"
420  mimeTypes[mime] = mimePtr->comment();
421  }
422  }
423 
424  QStringList mimeComments(mimeTypes.values());
425  mimeComments.sort();
426 
427  bool ok;
428  QString item;
429 
430  if (creatingNewArchive) {
431  item = KInputDialog::getItem(i18nc("@title:window", "Invalid Archive Type"),
432  i18nc("@info", "Ark cannot create archives of the type you have chosen.<nl/><nl/>Please choose another archive type below."),
433  mimeComments, 0, false, &ok);
434  } else {
435  item = KInputDialog::getItem(i18nc("@title:window", "Unable to Determine Archive Type"),
436  i18nc("@info", "Ark was unable to determine the archive type of the filename.<nl/><nl/>Please choose the correct archive type below."),
437  mimeComments,
438  0,
439  false,
440  &ok);
441  }
442 
443  if ((!ok) || (item.isEmpty())) {
444  return false;
445  }
446 
447  archive.reset(Kerfuffle::Archive::create(localFile, mimeTypes.key(item), m_model));
448  }
449 
450  if (!archive) {
451  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"));
452  return false;
453  }
454 
455  KJob *job = m_model->setArchive(archive.take());
456  registerJob(job);
457  job->start();
458  m_infoPanel->setIndex(QModelIndex());
459 
460  if (arguments().metaData()[QLatin1String( "showExtractDialog" )] == QLatin1String( "true" )) {
461  QTimer::singleShot(0, this, SLOT(slotExtractFiles()));
462  }
463 
464  return true;
465 }
466 
467 bool Part::saveFile()
468 {
469  return true;
470 }
471 
472 bool Part::isBusy() const
473 {
474  return m_busy;
475 }
476 
477 void Part::slotLoadingStarted()
478 {
479 }
480 
481 void Part::slotLoadingFinished(KJob *job)
482 {
483  kDebug();
484 
485  if (job->error()) {
486  if (arguments().metaData()[QLatin1String( "createNewArchive" )] != QLatin1String( "true" )) {
487  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"));
488  }
489  }
490 
491  m_view->sortByColumn(0, Qt::AscendingOrder);
492  m_view->expandToDepth(0);
493 
494  // After loading all files, resize the columns to fit all fields
495  m_view->header()->resizeSections(QHeaderView::ResizeToContents);
496 
497  updateActions();
498 }
499 
500 void Part::setReadyGui()
501 {
502  kDebug();
503  QApplication::restoreOverrideCursor();
504  m_busy = false;
505  m_view->setEnabled(true);
506  updateActions();
507 }
508 
509 void Part::setBusyGui()
510 {
511  kDebug();
512  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
513  m_busy = true;
514  m_view->setEnabled(false);
515  updateActions();
516 }
517 
518 void Part::setFileNameFromArchive()
519 {
520  const QString prettyName = url().fileName();
521 
522  m_infoPanel->setPrettyFileName(prettyName);
523  m_infoPanel->updateWithDefaults();
524 
525  emit setWindowCaption(prettyName);
526 }
527 
528 void Part::slotPreview()
529 {
530  slotPreview(m_view->selectionModel()->currentIndex());
531 }
532 
533 void Part::slotPreview(const QModelIndex & index)
534 {
535  if (!isPreviewable(index)) {
536  return;
537  }
538 
539  const ArchiveEntry& entry = m_model->entryForIndex(index);
540 
541  if (!entry.isEmpty()) {
542  Kerfuffle::ExtractionOptions options;
543  options[QLatin1String( "PreservePaths" )] = true;
544 
545  ExtractJob *job = m_model->extractFile(entry[ InternalID ], m_previewDir.name(), options);
546  registerJob(job);
547  connect(job, SIGNAL(result(KJob*)),
548  this, SLOT(slotPreviewExtracted(KJob*)));
549  job->start();
550  }
551 }
552 
553 void Part::slotPreviewExtracted(KJob *job)
554 {
555  // FIXME: the error checking here isn't really working
556  // if there's an error or an overwrite dialog,
557  // the preview dialog will be launched anyway
558  if (!job->error()) {
559  const ArchiveEntry& entry =
560  m_model->entryForIndex(m_view->selectionModel()->currentIndex());
561 
562  QString fullName =
563  m_previewDir.name() + QLatin1Char('/') + entry[FileName].toString();
564 
565  // Make sure a maliciously crafted archive with parent folders named ".." do
566  // not cause the previewed file path to be located outside the temporary
567  // directory, resulting in a directory traversal issue.
568  fullName.remove(QLatin1String("../"));
569 
570  ArkViewer::view(fullName, widget());
571  } else {
572  KMessageBox::error(widget(), job->errorString());
573  }
574  setReadyGui();
575 }
576 
577 void Part::slotError(const QString& errorMessage, const QString& details)
578 {
579  if (details.isEmpty()) {
580  KMessageBox::error(widget(), errorMessage);
581  } else {
582  KMessageBox::detailedError(widget(), errorMessage, details);
583  }
584 }
585 
586 bool Part::isSingleFolderArchive() const
587 {
588  return m_model->archive()->isSingleFolderArchive();
589 }
590 
591 QString Part::detectSubfolder() const
592 {
593  if (!m_model) {
594  return QString();
595  }
596 
597  return m_model->archive()->subfolderName();
598 }
599 
600 void Part::slotExtractFiles()
601 {
602  if (!m_model) {
603  return;
604  }
605 
606  QWeakPointer<Kerfuffle::ExtractionDialog> dialog = new Kerfuffle::ExtractionDialog;
607 
608  if (m_view->selectionModel()->selectedRows().count() > 0) {
609  dialog.data()->setShowSelectedFiles(true);
610  }
611 
612  dialog.data()->setSingleFolderArchive(isSingleFolderArchive());
613  dialog.data()->setSubfolder(detectSubfolder());
614 
615  dialog.data()->setCurrentUrl(QFileInfo(m_model->archive()->fileName()).path());
616 
617  if (dialog.data()->exec()) {
618  //this is done to update the quick extract menu
619  updateActions();
620 
621  QVariantList files;
622 
623  //if the user has chosen to extract only selected entries, fetch these
624  //from the listview
625  if (!dialog.data()->extractAllFiles()) {
626  files = selectedFilesWithChildren();
627  }
628 
629  kDebug() << "Selected " << files;
630 
631  Kerfuffle::ExtractionOptions options;
632 
633  if (dialog.data()->preservePaths()) {
634  options[QLatin1String("PreservePaths")] = true;
635  }
636 
637  options[QLatin1String("FollowExtractionDialogSettings")] = true;
638 
639  const QString destinationDirectory = dialog.data()->destinationDirectory().pathOrUrl();
640  ExtractJob *job = m_model->extractFiles(files, destinationDirectory, options);
641  registerJob(job);
642 
643  connect(job, SIGNAL(result(KJob*)),
644  this, SLOT(slotExtractionDone(KJob*)));
645 
646  job->start();
647  }
648 
649  delete dialog.data();
650 }
651 
652 QList<QVariant> Part::selectedFilesWithChildren()
653 {
654  Q_ASSERT(m_model);
655 
656  QModelIndexList toIterate = m_view->selectionModel()->selectedRows();
657 
658  for (int i = 0; i < toIterate.size(); ++i) {
659  QModelIndex index = toIterate.at(i);
660 
661  for (int j = 0; j < m_model->rowCount(index); ++j) {
662  QModelIndex child = m_model->index(j, 0, index);
663  if (!toIterate.contains(child)) {
664  toIterate << child;
665  }
666  }
667  }
668 
669  QVariantList ret;
670  foreach(const QModelIndex & index, toIterate) {
671  const ArchiveEntry& entry = m_model->entryForIndex(index);
672  if (entry.contains(InternalID)) {
673  ret << entry[ InternalID ];
674  }
675  }
676  return ret;
677 }
678 
679 QList<QVariant> Part::selectedFiles()
680 {
681  QStringList toSort;
682 
683  foreach(const QModelIndex & index, m_view->selectionModel()->selectedRows()) {
684  const ArchiveEntry& entry = m_model->entryForIndex(index);
685  toSort << entry[ InternalID ].toString();
686  }
687 
688  toSort.sort();
689  QVariantList ret;
690  foreach(const QString &i, toSort) {
691  ret << i;
692  }
693  return ret;
694 }
695 
696 void Part::slotExtractionDone(KJob* job)
697 {
698  kDebug();
699  if (job->error()) {
700  KMessageBox::error(widget(), job->errorString());
701  } else {
702  ExtractJob *extractJob = qobject_cast<ExtractJob*>(job);
703  Q_ASSERT(extractJob);
704 
705  const bool followExtractionDialogSettings =
706  extractJob->extractionOptions().value(QLatin1String("FollowExtractionDialogSettings"), false).toBool();
707  if (!followExtractionDialogSettings) {
708  return;
709  }
710 
711  if (ArkSettings::openDestinationFolderAfterExtraction()) {
712 
713  KUrl destinationDirectory(extractJob->destinationDirectory());
714  destinationDirectory.cleanPath();
715 
716  KRun::runUrl(destinationDirectory, QLatin1String("inode/directory"), widget());
717  }
718 
719  if (ArkSettings::closeAfterExtraction()) {
720  emit quit();
721  }
722  }
723 }
724 
725 void Part::adjustColumns()
726 {
727  kDebug();
728 
729  m_view->header()->setResizeMode(0, QHeaderView::ResizeToContents);
730 }
731 
732 void Part::slotAddFiles(const QStringList& filesToAdd, const QString& path)
733 {
734  if (filesToAdd.isEmpty()) {
735  return;
736  }
737 
738  kDebug() << "Adding " << filesToAdd << " to " << path;
739  kDebug() << "Warning, for now the path argument is not implemented";
740 
741  QStringList cleanFilesToAdd(filesToAdd);
742  for (int i = 0; i < cleanFilesToAdd.size(); ++i) {
743  QString& file = cleanFilesToAdd[i];
744  if (QFileInfo(file).isDir()) {
745  if (!file.endsWith(QLatin1Char( '/' ))) {
746  file += QLatin1Char( '/' );
747  }
748  }
749  }
750 
751  CompressionOptions options;
752 
753  QString firstPath = cleanFilesToAdd.first();
754  if (firstPath.right(1) == QLatin1String( "/" )) {
755  firstPath.chop(1);
756  }
757  firstPath = QFileInfo(firstPath).dir().absolutePath();
758 
759  kDebug() << "Detected relative path to be " << firstPath;
760  options[QLatin1String( "GlobalWorkDir" )] = firstPath;
761 
762  AddJob *job = m_model->addFiles(cleanFilesToAdd, options);
763  if (!job) {
764  return;
765  }
766 
767  connect(job, SIGNAL(result(KJob*)),
768  this, SLOT(slotAddFilesDone(KJob*)));
769  registerJob(job);
770  job->start();
771 }
772 
773 void Part::slotAddFiles()
774 {
775  kDebug();
776 
777  // #264819: passing widget() as the parent will not work as expected.
778  // KFileDialog will create a KFileWidget, which runs an internal
779  // event loop to stat the given directory. This, in turn, leads to
780  // events being delivered to widget(), which is a QSplitter, which
781  // in turn reimplements childEvent() and will end up calling
782  // QWidget::show() on the KFileDialog (thus showing it in a
783  // non-modal state).
784  // When KFileDialog::exec() is called, the widget is already shown
785  // and nothing happens.
786  const QStringList filesToAdd =
787  KFileDialog::getOpenFileNames(KUrl("kfiledialog:///ArkAddFiles"),
788  QString(), widget()->parentWidget(),
789  i18nc("@title:window", "Add Files"));
790 
791  slotAddFiles(filesToAdd);
792 }
793 
794 void Part::slotAddDir()
795 {
796  kDebug();
797  const QString dirToAdd = KFileDialog::getExistingDirectory(KUrl("kfiledialog:///ArkAddFiles"), widget(), i18nc("@title:window", "Add Folder"));
798 
799  if (!dirToAdd.isEmpty()) {
800  slotAddFiles(QStringList() << dirToAdd);
801  }
802 }
803 
804 void Part::slotAddFilesDone(KJob* job)
805 {
806  kDebug();
807  if (job->error()) {
808  KMessageBox::error(widget(), job->errorString());
809  }
810 }
811 
812 void Part::slotDeleteFilesDone(KJob* job)
813 {
814  kDebug();
815  if (job->error()) {
816  KMessageBox::error(widget(), job->errorString());
817  }
818 }
819 
820 void Part::slotDeleteFiles()
821 {
822  kDebug();
823 
824  const int reallyDelete =
825  KMessageBox::questionYesNo(NULL,
826  i18n("Deleting these files is not undoable. Are you sure you want to do this?"),
827  i18nc("@title:window", "Delete files"),
828  KStandardGuiItem::del(),
829  KStandardGuiItem::cancel(),
830  QString(),
831  KMessageBox::Dangerous | KMessageBox::Notify);
832 
833  if (reallyDelete == KMessageBox::No) {
834  return;
835  }
836 
837  DeleteJob *job = m_model->deleteFiles(selectedFilesWithChildren());
838  connect(job, SIGNAL(result(KJob*)),
839  this, SLOT(slotDeleteFilesDone(KJob*)));
840  registerJob(job);
841  job->start();
842 }
843 
844 void Part::slotToggleInfoPanel(bool visible)
845 {
846  QList<int> splitterSizes;
847 
848  if (visible) {
849  splitterSizes = ArkSettings::splitterSizesWithBothWidgets();
850  } else {
851  splitterSizes = m_splitter->sizes();
852  ArkSettings::setSplitterSizesWithBothWidgets(splitterSizes);
853  splitterSizes[1] = 0;
854  }
855 
856  m_splitter->setSizes(splitterSizes);
857  saveSplitterSizes();
858 }
859 
860 void Part::saveSplitterSizes()
861 {
862  ArkSettings::setSplitterSizes(m_splitter->sizes());
863  ArkSettings::self()->writeConfig();
864 }
865 
866 void Part::slotSaveAs()
867 {
868  KUrl saveUrl = KFileDialog::getSaveUrl(KUrl(QLatin1String( "kfiledialog:///ArkSaveAs/" ) + url().fileName()), QString(), widget());
869 
870  if ((saveUrl.isValid()) && (!saveUrl.isEmpty())) {
871  if (KIO::NetAccess::exists(saveUrl, KIO::NetAccess::DestinationSide, widget())) {
872  int overwrite = KMessageBox::warningContinueCancel(widget(),
873  i18nc("@info", "An archive named <filename>%1</filename> already exists. Are you sure you want to overwrite it?", saveUrl.fileName()),
874  QString(),
875  KStandardGuiItem::overwrite());
876 
877  if (overwrite != KMessageBox::Continue) {
878  return;
879  }
880  }
881 
882  KUrl srcUrl = KUrl::fromPath(localFilePath());
883 
884  if (!QFile::exists(localFilePath())) {
885  if (url().isLocalFile()) {
886  KMessageBox::error(widget(),
887  i18nc("@info", "The archive <filename>%1</filename> cannot be copied to the specified location. The archive does not exist anymore.", localFilePath()));
888 
889  return;
890  } else {
891  srcUrl = url();
892  }
893  }
894 
895  KIO::Job *copyJob = KIO::file_copy(srcUrl, saveUrl, -1, KIO::Overwrite);
896 
897  if (!KIO::NetAccess::synchronousRun(copyJob, widget())) {
898  KMessageBox::error(widget(),
899  i18nc("@info", "The archive could not be saved as <filename>%1</filename>. Try saving it to another location.", saveUrl.pathOrUrl()));
900  }
901  }
902 }
903 
904 } // namespace Ark
ArchiveModel::extractFile
Kerfuffle::ExtractJob * extractFile(const QVariant &fileName, const QString &destinationDir, const Kerfuffle::ExtractionOptions options=Kerfuffle::ExtractionOptions()) const
Definition: archivemodel.cpp:895
ArkViewer::view
static void view(const QString &fileName, QWidget *parent=0)
Definition: arkviewer.cpp:89
Ark::Part::isBusy
bool isBusy() const
Definition: part.cpp:472
Kerfuffle::Job::start
void start()
Definition: jobs.cpp:103
archivemodel.h
archiveview.h
Kerfuffle::IsDirectory
The entry is a directory.
Definition: archive.h:72
Kerfuffle::supportedWriteMimeTypes
QStringList supportedWriteMimeTypes()
Definition: archive.cpp:306
Kerfuffle::DeleteJob
Definition: jobs.h:155
Kerfuffle::Archive::subfolderName
QString subfolderName()
Definition: archive.cpp:268
Kerfuffle::ExtractionDialog::setShowSelectedFiles
void setShowSelectedFiles(bool)
Definition: extractiondialog.cpp:152
ArkSettings::openDestinationFolderAfterExtraction
static bool openDestinationFolderAfterExtraction()
Get Open destination folder after extraction.
Definition: settings.h:32
InfoPanel::setPrettyFileName
void setPrettyFileName(const QString &fileName)
Sets a different file name for the current open archive.
Definition: infopanel.cpp:90
Kerfuffle::AddJob
Definition: jobs.h:140
InfoPanel::setIndex
void setIndex(const QModelIndex &)
Definition: infopanel.cpp:95
Kerfuffle::ExtractionDialog
Definition: extractiondialog.h:40
archive.h
arkviewer.h
ArchiveModel::index
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
Definition: archivemodel.cpp:425
QObject
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
ArchiveModel
Definition: archivemodel.h:41
Ark::Part::extractSelectedFilesTo
void extractSelectedFilesTo(const QString &localPath)
Definition: part.cpp:161
Ark::Part::quit
void quit()
Ark::Part::createAboutData
static KAboutData * createAboutData()
Definition: part.cpp:370
Kerfuffle::ArchiveEntry
QHash< int, QVariant > ArchiveEntry
Definition: archive.h:78
InfoPanel::updateWithDefaults
void updateWithDefaults()
Definition: infopanel.cpp:61
Kerfuffle::supportedMimeTypes
QStringList supportedMimeTypes()
Definition: archive.cpp:279
Kerfuffle::Archive::isSingleFolderArchive
bool isSingleFolderArchive()
Definition: archive.cpp:256
jobtracker.h
ArkSettings::self
static ArkSettings * self()
Definition: settings.cpp:19
ArchiveModel::setArchive
KJob * setArchive(Kerfuffle::Archive *archive)
Definition: archivemodel.cpp:863
K_PLUGIN_FACTORY
K_PLUGIN_FACTORY(ExtractHerePluginFactory, registerPlugin< ExtractHereDndPlugin >();) void ExtractHereDndPlugin
Definition: extractHereDndPlugin.cpp:33
InfoPanel
Definition: infopanel.h:31
Kerfuffle::CompressionOptions
QHash< QString, QVariant > CompressionOptions
These are the extra options for doing the compression.
Definition: archive.h:85
Ark::Part::ready
void ready()
Kerfuffle::ExtractJob::extractionOptions
ExtractionOptions extractionOptions() const
Definition: jobs.cpp:292
ArchiveModel::archive
Kerfuffle::Archive * archive() const
Definition: archivemodel.cpp:858
Kerfuffle::Archive::fileName
QString fileName() const
Definition: archive.cpp:194
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
ArkSettings::setSplitterSizes
static void setSplitterSizes(const QList< int > &v)
Set splitterSizes.
Definition: settings.h:79
Kerfuffle::ExtractJob::destinationDirectory
QString destinationDirectory() const
Definition: jobs.cpp:287
Kerfuffle::ExtractJob
Definition: jobs.h:118
Ark::Part::saveFile
virtual bool saveFile()
Definition: part.cpp:467
ArkSettings::setSplitterSizesWithBothWidgets
static void setSplitterSizesWithBothWidgets(const QList< int > &v)
Set splitterSizesWithBothWidgets.
Definition: settings.h:98
jobs.h
settings.h
Kerfuffle::ExtractionOptions
QHash< QString, QVariant > ExtractionOptions
Definition: archive.h:86
ArchiveModel::rowCount
int rowCount(const QModelIndex &parent=QModelIndex()) const
Definition: archivemodel.cpp:485
Ark::Part::openFile
virtual bool openFile()
Definition: part.cpp:375
InfoPanel::setIndexes
void setIndexes(const QModelIndexList &list)
Definition: infopanel.cpp:136
ArkSettings::splitterSizes
static QList< int > splitterSizes()
Get splitterSizes.
Definition: settings.h:89
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
Ark::s_instanceCounter
static quint32 s_instanceCounter
Definition: part.cpp:78
ArkSettings::splitterSizesWithBothWidgets
static QList< int > splitterSizesWithBothWidgets()
Get splitterSizesWithBothWidgets.
Definition: settings.h:108
Ark::Part::busy
void busy()
Ark::Part::~Part
~Part()
Definition: part.cpp:138
KJob
infopanel.h
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
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:08:10 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
  • kremotecontrol
  • ktimer
  • kwallet
  • superkaramba
  • 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