2014-11-18 17:46:34 +00:00
/*
* ark - - archiver for the KDE project
*
* Copyright ( C ) 2007 Henrique Pinto < henrique . pinto @ kdemail . net >
* Copyright ( C ) 2008 - 2009 Harald Hvaal < haraldhv @ stud . ntnu . no >
* Copyright ( C ) 2009 - 2012 Raphael Kubo da Costa < rakuco @ FreeBSD . org >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
*/
# include "part.h"
# include "archivemodel.h"
# include "archiveview.h"
# include "arkviewer.h"
# include "dnddbusinterfaceadaptor.h"
# include "infopanel.h"
# include "jobtracker.h"
# include "kerfuffle/archive.h"
# include "kerfuffle/extractiondialog.h"
# include "kerfuffle/jobs.h"
# include "kerfuffle/settings.h"
# include <KAboutData>
# include <KAction>
# include <KActionCollection>
# include <KApplication>
# include <KConfigGroup>
# include <KDebug>
# include <KFileDialog>
# include <KGuiItem>
# include <KIO/Job>
# include <KIO/NetAccess>
2024-05-18 03:08:36 +03:00
# include <KIO/RenameDialog>
2014-11-18 17:46:34 +00:00
# include <KIcon>
# include <KInputDialog>
2014-12-21 12:48:25 +00:00
# include <KMenu>
2014-11-18 17:46:34 +00:00
# include <KMessageBox>
# include <KPluginFactory>
# include <KRun>
2024-05-12 03:22:20 +03:00
# include <KToolInvocation>
2014-11-18 17:46:34 +00:00
# include <KSelectAction>
# include <KStandardDirs>
# include <KStandardGuiItem>
# include <KTempDir>
# include <KToggleAction>
2014-12-21 12:48:25 +00:00
# include <KXMLGUIFactory>
2014-11-18 17:46:34 +00:00
# include <QAction>
# include <QCursor>
# include <QHeaderView>
# include <QMenu>
# include <QMimeData>
2015-11-25 10:00:54 +00:00
# include <QtGui/qevent.h>
2014-11-18 17:46:34 +00:00
# include <QScopedPointer>
# include <QSplitter>
# include <QTimer>
# include <QVBoxLayout>
2015-11-25 10:00:54 +00:00
# include <QtCore/qsharedpointer.h>
2014-11-18 17:46:34 +00:00
# include <QtDBus/QtDBus>
using namespace Kerfuffle ;
K_PLUGIN_FACTORY ( Factory , registerPlugin < Ark : : Part > ( ) ; )
K_EXPORT_PLUGIN ( Factory ( " ark " ) )
namespace Ark
{
static quint32 s_instanceCounter = 1 ;
Part : : Part ( QWidget * parentWidget , QObject * parent , const QVariantList & args )
: KParts : : ReadWritePart ( parent ) ,
m_splitter ( 0 ) ,
m_busy ( false ) ,
m_jobTracker ( 0 )
{
Q_UNUSED ( args )
2022-11-26 23:26:42 +02:00
setObjectName ( QString : : fromLatin1 ( " ArkPart " ) ) ;
2022-09-21 14:32:58 +03:00
setComponentData ( Factory : : componentData ( ) ) ;
2014-11-18 17:46:34 +00:00
new DndExtractAdaptor ( this ) ;
const QString pathName = QString ( QLatin1String ( " /DndExtract/%1 " ) ) . arg ( s_instanceCounter + + ) ;
if ( ! QDBusConnection : : sessionBus ( ) . registerObject ( pathName , this ) ) {
kFatal ( ) < < " Could not register a D-Bus object for drag'n'drop " ;
}
m_model = new ArchiveModel ( pathName , this ) ;
m_splitter = new QSplitter ( Qt : : Horizontal , parentWidget ) ;
setWidget ( m_splitter ) ;
m_view = new ArchiveView ;
m_infoPanel = new InfoPanel ( m_model ) ;
m_splitter - > addWidget ( m_view ) ;
m_splitter - > addWidget ( m_infoPanel ) ;
QList < int > splitterSizes = ArkSettings : : splitterSizes ( ) ;
if ( splitterSizes . isEmpty ( ) ) {
splitterSizes . append ( 200 ) ;
splitterSizes . append ( 100 ) ;
}
m_splitter - > setSizes ( splitterSizes ) ;
setupView ( ) ;
setupActions ( ) ;
connect ( m_model , SIGNAL ( loadingStarted ( ) ) ,
this , SLOT ( slotLoadingStarted ( ) ) ) ;
connect ( m_model , SIGNAL ( loadingFinished ( KJob * ) ) ,
this , SLOT ( slotLoadingFinished ( KJob * ) ) ) ;
connect ( m_model , SIGNAL ( droppedFiles ( QStringList , QString ) ) ,
this , SLOT ( slotAddFiles ( QStringList , QString ) ) ) ;
connect ( m_model , SIGNAL ( error ( QString , QString ) ) ,
this , SLOT ( slotError ( QString , QString ) ) ) ;
connect ( this , SIGNAL ( busy ( ) ) ,
this , SLOT ( setBusyGui ( ) ) ) ;
connect ( this , SIGNAL ( ready ( ) ) ,
this , SLOT ( setReadyGui ( ) ) ) ;
connect ( this , SIGNAL ( completed ( ) ) ,
this , SLOT ( setFileNameFromArchive ( ) ) ) ;
setXMLFile ( QLatin1String ( " ark_part.rc " ) ) ;
}
Part : : ~ Part ( )
{
2014-12-21 12:48:25 +00:00
qDeleteAll ( m_previewDirList ) ;
2014-11-18 17:46:34 +00:00
saveSplitterSizes ( ) ;
m_extractFilesAction - > menu ( ) - > deleteLater ( ) ;
}
void Part : : registerJob ( KJob * job )
{
if ( ! m_jobTracker ) {
m_jobTracker = new JobTracker ( widget ( ) ) ;
m_jobTracker - > widget ( job ) - > show ( ) ;
}
m_jobTracker - > registerJob ( job ) ;
emit busy ( ) ;
connect ( job , SIGNAL ( result ( KJob * ) ) , this , SIGNAL ( ready ( ) ) ) ;
}
// TODO: One should construct a KUrl out of localPath in order to be able to handle
// non-local destinations (ie. trash:/ or a remote location)
// See bugs #189322 and #204323.
void Part : : extractSelectedFilesTo ( const QString & localPath )
{
kDebug ( ) < < " Extract to " < < localPath ;
if ( ! m_model ) {
return ;
}
if ( m_view - > selectionModel ( ) - > selectedRows ( ) . count ( ) ! = 1 ) {
m_view - > selectionModel ( ) - > setCurrentIndex ( m_view - > currentIndex ( ) , QItemSelectionModel : : ClearAndSelect | QItemSelectionModel : : Rows ) ;
}
if ( m_view - > selectionModel ( ) - > selectedRows ( ) . count ( ) ! = 1 ) {
return ;
}
QVariant internalRoot ;
kDebug ( ) < < " valid " < < m_view - > currentIndex ( ) . parent ( ) . isValid ( ) ;
if ( m_view - > currentIndex ( ) . parent ( ) . isValid ( ) ) {
internalRoot = m_model - > entryForIndex ( m_view - > currentIndex ( ) . parent ( ) ) . value ( InternalID ) ;
}
if ( internalRoot . isNull ( ) ) {
//we have the special case valid parent, but the parent does not
//actually correspond to an item in the archive, but an automatically
//created folder. for now, we will just use the filename of the node
//instead, but for plugins that rely on a non-filename value as the
//InternalId, this WILL break things. TODO find a solution
internalRoot = m_model - > entryForIndex ( m_view - > currentIndex ( ) . parent ( ) ) . value ( FileName ) ;
}
QList < QVariant > files = selectedFilesWithChildren ( ) ;
if ( files . isEmpty ( ) ) {
return ;
}
kDebug ( ) < < " selected files are " < < files ;
Kerfuffle : : ExtractionOptions options ;
options [ QLatin1String ( " PreservePaths " ) ] = true ;
if ( ! internalRoot . isNull ( ) ) {
options [ QLatin1String ( " RootNode " ) ] = internalRoot ;
}
ExtractJob * job = m_model - > extractFiles ( files , localPath , options ) ;
registerJob ( job ) ;
connect ( job , SIGNAL ( result ( KJob * ) ) ,
this , SLOT ( slotExtractionDone ( KJob * ) ) ) ;
job - > start ( ) ;
}
void Part : : setupView ( )
{
2014-12-21 12:48:25 +00:00
m_view - > setContextMenuPolicy ( Qt : : CustomContextMenu ) ;
2014-11-18 17:46:34 +00:00
m_view - > setModel ( m_model ) ;
m_view - > setSortingEnabled ( true ) ;
connect ( m_view - > selectionModel ( ) , SIGNAL ( selectionChanged ( QItemSelection , QItemSelection ) ) ,
this , SLOT ( updateActions ( ) ) ) ;
connect ( m_view - > selectionModel ( ) , SIGNAL ( selectionChanged ( QItemSelection , QItemSelection ) ) ,
this , SLOT ( selectionChanged ( ) ) ) ;
//TODO: fix an actual eventhandler
2015-01-04 02:45:51 +00:00
connect ( m_view , SIGNAL ( activated ( QModelIndex ) ) ,
2014-12-21 12:48:25 +00:00
this , SLOT ( slotPreviewWithInternalViewer ( ) ) ) ;
connect ( m_view , SIGNAL ( customContextMenuRequested ( QPoint ) ) , this , SLOT ( slotShowContextMenu ( ) ) ) ;
2014-11-18 17:46:34 +00:00
connect ( m_model , SIGNAL ( columnsInserted ( QModelIndex , int , int ) ) ,
this , SLOT ( adjustColumns ( ) ) ) ;
}
void Part : : setupActions ( )
{
KToggleAction * showInfoPanelAction = new KToggleAction ( i18nc ( " @action:inmenu " , " Show information panel " ) , this ) ;
actionCollection ( ) - > addAction ( QLatin1String ( " show-infopanel " ) , showInfoPanelAction ) ;
showInfoPanelAction - > setChecked ( m_splitter - > sizes ( ) . at ( 1 ) > 0 ) ;
connect ( showInfoPanelAction , SIGNAL ( triggered ( bool ) ) ,
this , SLOT ( slotToggleInfoPanel ( bool ) ) ) ;
m_saveAsAction = KStandardAction : : saveAs ( this , SLOT ( slotSaveAs ( ) ) , actionCollection ( ) ) ;
2014-12-21 12:48:25 +00:00
m_previewChooseAppAction = actionCollection ( ) - > addAction ( QLatin1String ( " openwith " ) ) ;
m_previewChooseAppAction - > setText ( i18nc ( " open a file with external program " , " Open &With... " ) ) ;
m_previewChooseAppAction - > setIcon ( KIcon ( QLatin1String ( " document-open " ) ) ) ;
m_previewChooseAppAction - > setStatusTip ( i18n ( " Click to open the selected file with an external program " ) ) ;
connect ( m_previewChooseAppAction , SIGNAL ( triggered ( bool ) ) , this , SLOT ( slotPreviewWithExternalProgram ( ) ) ) ;
2014-11-18 17:46:34 +00:00
m_previewAction = actionCollection ( ) - > addAction ( QLatin1String ( " preview " ) ) ;
m_previewAction - > setText ( i18nc ( " to preview a file inside an archive " , " Pre&view " ) ) ;
m_previewAction - > setIcon ( KIcon ( QLatin1String ( " document-preview-archive " ) ) ) ;
m_previewAction - > setStatusTip ( i18n ( " Click to preview the selected file " ) ) ;
2024-04-23 08:02:39 +03:00
m_previewAction - > setShortcut ( QKeySequence ( Qt : : Key_Return , Qt : : Key_Space ) ) ;
2014-11-18 17:46:34 +00:00
connect ( m_previewAction , SIGNAL ( triggered ( bool ) ) ,
2014-12-21 12:48:25 +00:00
this , SLOT ( slotPreviewWithInternalViewer ( ) ) ) ;
2014-11-18 17:46:34 +00:00
m_extractFilesAction = actionCollection ( ) - > addAction ( QLatin1String ( " extract " ) ) ;
m_extractFilesAction - > setText ( i18n ( " E&xtract " ) ) ;
m_extractFilesAction - > setIcon ( KIcon ( QLatin1String ( " archive-extract " ) ) ) ;
m_extractFilesAction - > setStatusTip ( i18n ( " Click to open an extraction dialog, where you can choose to extract either all files or just the selected ones " ) ) ;
2023-08-30 20:56:41 +03:00
m_extractFilesAction - > setShortcut ( QKeySequence ( Qt : : CTRL + Qt : : Key_E ) ) ;
2014-11-18 17:46:34 +00:00
connect ( m_extractFilesAction , SIGNAL ( triggered ( bool ) ) ,
this , SLOT ( slotExtractFiles ( ) ) ) ;
m_addFilesAction = actionCollection ( ) - > addAction ( QLatin1String ( " add " ) ) ;
m_addFilesAction - > setIcon ( KIcon ( QLatin1String ( " archive-insert " ) ) ) ;
m_addFilesAction - > setText ( i18n ( " Add &File... " ) ) ;
m_addFilesAction - > setStatusTip ( i18n ( " Click to add files to the archive " ) ) ;
connect ( m_addFilesAction , SIGNAL ( triggered ( bool ) ) ,
this , SLOT ( slotAddFiles ( ) ) ) ;
m_addDirAction = actionCollection ( ) - > addAction ( QLatin1String ( " add-dir " ) ) ;
m_addDirAction - > setIcon ( KIcon ( QLatin1String ( " archive-insert-directory " ) ) ) ;
m_addDirAction - > setText ( i18n ( " Add Fo&lder... " ) ) ;
m_addDirAction - > setStatusTip ( i18n ( " Click to add a folder to the archive " ) ) ;
connect ( m_addDirAction , SIGNAL ( triggered ( bool ) ) ,
this , SLOT ( slotAddDir ( ) ) ) ;
m_deleteFilesAction = actionCollection ( ) - > addAction ( QLatin1String ( " delete " ) ) ;
m_deleteFilesAction - > setIcon ( KIcon ( QLatin1String ( " archive-remove " ) ) ) ;
m_deleteFilesAction - > setText ( i18n ( " De&lete " ) ) ;
m_deleteFilesAction - > setShortcut ( Qt : : Key_Delete ) ;
m_deleteFilesAction - > setStatusTip ( i18n ( " Click to delete the selected files " ) ) ;
connect ( m_deleteFilesAction , SIGNAL ( triggered ( bool ) ) ,
this , SLOT ( slotDeleteFiles ( ) ) ) ;
updateActions ( ) ;
}
void Part : : updateActions ( )
{
bool isWritable = m_model - > archive ( ) & & ( ! m_model - > archive ( ) - > isReadOnly ( ) ) ;
m_previewAction - > setEnabled ( ! isBusy ( ) & & ( m_view - > selectionModel ( ) - > selectedRows ( ) . count ( ) = = 1 )
& & isPreviewable ( m_view - > selectionModel ( ) - > currentIndex ( ) ) ) ;
m_extractFilesAction - > setEnabled ( ! isBusy ( ) & & ( m_model - > rowCount ( ) > 0 ) ) ;
m_addFilesAction - > setEnabled ( ! isBusy ( ) & & isWritable ) ;
m_addDirAction - > setEnabled ( ! isBusy ( ) & & isWritable ) ;
m_deleteFilesAction - > setEnabled ( ! isBusy ( ) & & ( m_view - > selectionModel ( ) - > selectedRows ( ) . count ( ) > 0 )
& & isWritable ) ;
2014-12-21 12:48:25 +00:00
m_previewChooseAppAction - > setEnabled ( ! isBusy ( ) & & ( m_view - > selectionModel ( ) - > selectedRows ( ) . count ( ) > 0 )
& & isWritable ) ;
2014-11-18 17:46:34 +00:00
QMenu * menu = m_extractFilesAction - > menu ( ) ;
if ( ! menu ) {
menu = new QMenu ;
m_extractFilesAction - > setMenu ( menu ) ;
connect ( menu , SIGNAL ( triggered ( QAction * ) ) ,
this , SLOT ( slotQuickExtractFiles ( QAction * ) ) ) ;
// Remember to keep this action's properties as similar to
// m_extractFilesAction's as possible (except where it does not make
// sense, such as the text or the shortcut).
QAction * extractTo = menu - > addAction ( i18n ( " Extract To... " ) ) ;
extractTo - > setIcon ( m_extractFilesAction - > icon ( ) ) ;
extractTo - > setStatusTip ( m_extractFilesAction - > statusTip ( ) ) ;
connect ( extractTo , SIGNAL ( triggered ( bool ) ) , SLOT ( slotExtractFiles ( ) ) ) ;
menu - > addSeparator ( ) ;
QAction * header = menu - > addAction ( i18n ( " Quick Extract To... " ) ) ;
header - > setEnabled ( false ) ;
header - > setIcon ( KIcon ( QLatin1String ( " archive-extract " ) ) ) ;
}
while ( menu - > actions ( ) . size ( ) > 3 ) {
menu - > removeAction ( menu - > actions ( ) . last ( ) ) ;
}
const KConfigGroup conf ( KGlobal : : config ( ) , " DirSelect Dialog " ) ;
const QStringList dirHistory = conf . readPathEntry ( " History Items " , QStringList ( ) ) ;
for ( int i = 0 ; i < qMin ( 10 , dirHistory . size ( ) ) ; + + i ) {
const KUrl dirUrl ( dirHistory . at ( i ) ) ;
QAction * newAction = menu - > addAction ( dirUrl . pathOrUrl ( ) ) ;
newAction - > setData ( dirUrl . pathOrUrl ( ) ) ;
}
}
void Part : : slotQuickExtractFiles ( QAction * triggeredAction )
{
// #190507: triggeredAction->data.isNull() means it's the "Extract to..."
// action, and we do not want it to run here
if ( ! triggeredAction - > data ( ) . isNull ( ) ) {
kDebug ( ) < < " Extract to " < < triggeredAction - > data ( ) . toString ( ) ;
const QString userDestination = triggeredAction - > data ( ) . toString ( ) ;
QString finalDestinationDirectory ;
const QString detectedSubfolder = detectSubfolder ( ) ;
if ( ! isSingleFolderArchive ( ) ) {
finalDestinationDirectory = userDestination +
QDir : : separator ( ) + detectedSubfolder ;
QDir ( userDestination ) . mkdir ( detectedSubfolder ) ;
} else {
finalDestinationDirectory = userDestination ;
}
Kerfuffle : : ExtractionOptions options ;
options [ QLatin1String ( " PreservePaths " ) ] = true ;
QList < QVariant > files = selectedFiles ( ) ;
ExtractJob * job = m_model - > extractFiles ( files , finalDestinationDirectory , options ) ;
registerJob ( job ) ;
connect ( job , SIGNAL ( result ( KJob * ) ) ,
this , SLOT ( slotExtractionDone ( KJob * ) ) ) ;
job - > start ( ) ;
}
}
bool Part : : isPreviewable ( const QModelIndex & index ) const
{
return index . isValid ( ) & & ( ! m_model - > entryForIndex ( index ) [ IsDirectory ] . toBool ( ) ) ;
}
void Part : : selectionChanged ( )
{
m_infoPanel - > setIndexes ( m_view - > selectionModel ( ) - > selectedRows ( ) ) ;
}
KAboutData * Part : : createAboutData ( )
{
return new KAboutData ( " ark " , 0 , ki18n ( " ArkPart " ) , " 3.0 " ) ;
}
bool Part : : openFile ( )
{
2024-05-16 04:36:33 +03:00
const QString localFile = localFilePath ( ) ;
2014-11-18 17:46:34 +00:00
const QFileInfo localFileInfo ( localFile ) ;
const bool creatingNewArchive =
arguments ( ) . metaData ( ) [ QLatin1String ( " createNewArchive " ) ] = = QLatin1String ( " true " ) ;
if ( localFileInfo . isDir ( ) ) {
2024-05-16 04:36:33 +03:00
KMessageBox : : error (
NULL ,
i18nc ( " @info " , " <tt>%1</tt> is a directory. " , localFile )
) ;
2014-11-18 17:46:34 +00:00
return false ;
}
if ( creatingNewArchive ) {
if ( localFileInfo . exists ( ) ) {
2024-05-16 04:36:33 +03:00
int overwrite = KMessageBox : : questionYesNo (
NULL ,
i18nc ( " @info " , " The archive <tt>%1</tt> already exists. Would you like to open it instead? " , localFile ) ,
i18nc ( " @title:window " , " File Exists " ) , KGuiItem ( i18n ( " Open File " ) ) , KStandardGuiItem : : cancel ( )
) ;
2014-11-18 17:46:34 +00:00
if ( overwrite = = KMessageBox : : No ) {
return false ;
}
}
} else {
if ( ! localFileInfo . exists ( ) ) {
2024-05-16 04:36:33 +03:00
KMessageBox : : sorry (
NULL ,
i18nc ( " @info " , " The archive <tt>%1</tt> was not found. " , localFile ) ,
i18nc ( " @title:window " , " Error Opening Archive " )
) ;
2014-11-18 17:46:34 +00:00
return false ;
}
}
QScopedPointer < Kerfuffle : : Archive > archive ( Kerfuffle : : Archive : : create ( localFile , m_model ) ) ;
if ( ( ! archive ) | | ( ( creatingNewArchive ) & & ( archive - > isReadOnly ( ) ) ) ) {
QStringList mimeTypeList ;
QHash < QString , QString > mimeTypes ;
if ( creatingNewArchive ) {
mimeTypeList = Kerfuffle : : supportedWriteMimeTypes ( ) ;
} else {
mimeTypeList = Kerfuffle : : supportedMimeTypes ( ) ;
}
foreach ( const QString & mime , mimeTypeList ) {
KMimeType : : Ptr mimePtr ( KMimeType : : mimeType ( mime ) ) ;
if ( mimePtr ) {
// Key = "application/zip", Value = "Zip Archive"
mimeTypes [ mime ] = mimePtr - > comment ( ) ;
}
}
QStringList mimeComments ( mimeTypes . values ( ) ) ;
mimeComments . sort ( ) ;
2024-05-16 04:36:33 +03:00
bool ok = false ;
2014-11-18 17:46:34 +00:00
QString item ;
if ( creatingNewArchive ) {
2023-08-17 23:41:32 +03:00
item = KInputDialog : : getItem (
i18nc ( " @title:window " , " Invalid Archive Type " ) ,
i18nc ( " @info " , " Ark cannot create archives of the type you have chosen.<br/><br/>Please choose another archive type below. " ) ,
mimeComments , 0 , false , & ok
) ;
2014-11-18 17:46:34 +00:00
} else {
2023-08-17 23:41:32 +03:00
item = KInputDialog : : getItem (
i18nc ( " @title:window " , " Unable to Determine Archive Type " ) ,
i18nc ( " @info " , " Ark was unable to determine the archive type of the filename.<br/><br/>Please choose the correct archive type below. " ) ,
mimeComments , 0 , false , & ok
) ;
2014-11-18 17:46:34 +00:00
}
if ( ( ! ok ) | | ( item . isEmpty ( ) ) ) {
return false ;
}
archive . reset ( Kerfuffle : : Archive : : create ( localFile , mimeTypes . key ( item ) , m_model ) ) ;
}
if ( ! archive ) {
2024-05-16 04:36:33 +03:00
KMessageBox : : sorry (
NULL ,
i18nc ( " @info " , " Ark was not able to open the archive <tt>%1</tt>. No plugin capable of handling the file was found. " , localFile ) ,
i18nc ( " @title:window " , " Error Opening Archive " )
) ;
2014-11-18 17:46:34 +00:00
return false ;
}
KJob * job = m_model - > setArchive ( archive . take ( ) ) ;
registerJob ( job ) ;
job - > start ( ) ;
m_infoPanel - > setIndex ( QModelIndex ( ) ) ;
if ( arguments ( ) . metaData ( ) [ QLatin1String ( " showExtractDialog " ) ] = = QLatin1String ( " true " ) ) {
QTimer : : singleShot ( 0 , this , SLOT ( slotExtractFiles ( ) ) ) ;
}
return true ;
}
bool Part : : saveFile ( )
{
return true ;
}
bool Part : : isBusy ( ) const
{
return m_busy ;
}
void Part : : slotLoadingStarted ( )
{
}
void Part : : slotLoadingFinished ( KJob * job )
{
2024-05-16 03:34:03 +03:00
if ( job - > error ( ) ! = KJob : : NoError ) {
2014-11-18 17:46:34 +00:00
if ( arguments ( ) . metaData ( ) [ QLatin1String ( " createNewArchive " ) ] ! = QLatin1String ( " true " ) ) {
2024-05-16 04:36:33 +03:00
KMessageBox : : sorry (
NULL ,
i18nc ( " @info " , " Loading the archive <tt>%1</tt> failed with the following error: <i>%2</i> " , localFilePath ( ) , job - > errorText ( ) ) ,
i18nc ( " @title:window " , " Error Opening Archive " )
) ;
2014-11-18 17:46:34 +00:00
// The file failed to open, so reset the open archive, info panel and caption.
m_model - > setArchive ( NULL ) ;
m_infoPanel - > setPrettyFileName ( QString ( ) ) ;
m_infoPanel - > updateWithDefaults ( ) ;
emit setWindowCaption ( QString ( ) ) ;
}
}
m_view - > sortByColumn ( 0 , Qt : : AscendingOrder ) ;
m_view - > expandToDepth ( 0 ) ;
// After loading all files, resize the columns to fit all fields
m_view - > header ( ) - > resizeSections ( QHeaderView : : ResizeToContents ) ;
updateActions ( ) ;
}
void Part : : setReadyGui ( )
{
QApplication : : restoreOverrideCursor ( ) ;
m_busy = false ;
m_view - > setEnabled ( true ) ;
updateActions ( ) ;
}
void Part : : setBusyGui ( )
{
QApplication : : setOverrideCursor ( QCursor ( Qt : : WaitCursor ) ) ;
m_busy = true ;
m_view - > setEnabled ( false ) ;
updateActions ( ) ;
}
void Part : : setFileNameFromArchive ( )
{
const QString prettyName = url ( ) . fileName ( ) ;
m_infoPanel - > setPrettyFileName ( prettyName ) ;
m_infoPanel - > updateWithDefaults ( ) ;
emit setWindowCaption ( prettyName ) ;
}
2014-12-21 12:48:25 +00:00
void Part : : slotPreviewWithInternalViewer ( )
2014-11-18 17:46:34 +00:00
{
2014-12-21 12:48:25 +00:00
preview ( m_view - > selectionModel ( ) - > currentIndex ( ) , InternalViewer ) ;
2014-11-18 17:46:34 +00:00
}
2014-12-21 12:48:25 +00:00
void Part : : slotPreviewWithExternalProgram ( )
{
preview ( m_view - > selectionModel ( ) - > currentIndex ( ) , ExternalProgram ) ;
}
void Part : : preview ( const QModelIndex & index , PreviewMode mode )
2014-11-18 17:46:34 +00:00
{
if ( ! isPreviewable ( index ) ) {
return ;
}
const ArchiveEntry & entry = m_model - > entryForIndex ( index ) ;
if ( ! entry . isEmpty ( ) ) {
Kerfuffle : : ExtractionOptions options ;
options [ QLatin1String ( " PreservePaths " ) ] = true ;
2014-12-21 12:48:25 +00:00
m_previewDirList . append ( new KTempDir ) ;
m_previewMode = mode ;
ExtractJob * job = m_model - > extractFile ( entry [ InternalID ] , m_previewDirList . last ( ) - > name ( ) , options ) ;
2014-11-18 17:46:34 +00:00
registerJob ( job ) ;
connect ( job , SIGNAL ( result ( KJob * ) ) ,
this , SLOT ( slotPreviewExtracted ( KJob * ) ) ) ;
job - > start ( ) ;
}
}
void Part : : slotPreviewExtracted ( KJob * job )
{
// FIXME: the error checking here isn't really working
// if there's an error or an overwrite dialog,
// the preview dialog will be launched anyway
if ( ! job - > error ( ) ) {
2024-05-16 04:36:33 +03:00
const ArchiveEntry & entry = m_model - > entryForIndex ( m_view - > selectionModel ( ) - > currentIndex ( ) ) ;
2014-11-18 17:46:34 +00:00
2014-12-21 12:48:25 +00:00
ExtractJob * extractJob = qobject_cast < ExtractJob * > ( job ) ;
Q_ASSERT ( extractJob ) ;
QString fullName = extractJob - > destinationDirectory ( ) + entry [ FileName ] . toString ( ) ;
2014-11-18 17:46:34 +00:00
// Make sure a maliciously crafted archive with parent folders named ".." do
// not cause the previewed file path to be located outside the temporary
// directory, resulting in a directory traversal issue.
fullName . remove ( QLatin1String ( " ../ " ) ) ;
2014-12-21 12:48:25 +00:00
// TODO: get rid of m_previewMode by extending ExtractJob with a PreviewJob.
// This would prevent race conditions if we ever stop disabling
// the whole UI while extracting a file to preview it.
switch ( m_previewMode ) {
case InternalViewer :
ArkViewer : : view ( fullName , widget ( ) ) ;
break ;
case ExternalProgram :
KUrl : : List list ;
list . append ( KUrl ( fullName ) ) ;
KRun : : displayOpenWithDialog ( list , widget ( ) , true ) ;
break ;
}
2014-11-18 17:46:34 +00:00
} else {
KMessageBox : : error ( widget ( ) , job - > errorString ( ) ) ;
}
setReadyGui ( ) ;
}
void Part : : slotError ( const QString & errorMessage , const QString & details )
{
if ( details . isEmpty ( ) ) {
KMessageBox : : error ( widget ( ) , errorMessage ) ;
} else {
KMessageBox : : detailedError ( widget ( ) , errorMessage , details ) ;
}
}
bool Part : : isSingleFolderArchive ( ) const
{
return m_model - > archive ( ) - > isSingleFolderArchive ( ) ;
}
QString Part : : detectSubfolder ( ) const
{
if ( ! m_model ) {
return QString ( ) ;
}
return m_model - > archive ( ) - > subfolderName ( ) ;
}
void Part : : slotExtractFiles ( )
{
if ( ! m_model ) {
return ;
}
2024-05-18 03:08:36 +03:00
QVariantList files = selectedFilesWithChildren ( ) ;
kDebug ( ) < < " Selected " < < files ;
2014-11-18 17:46:34 +00:00
2024-05-18 03:08:36 +03:00
QWeakPointer < Kerfuffle : : ExtractionDialog > dialog = new Kerfuffle : : ExtractionDialog ( ) ;
2014-11-18 17:46:34 +00:00
2023-07-03 03:12:02 +03:00
dialog . data ( ) - > setUrl ( QFileInfo ( m_model - > archive ( ) - > fileName ( ) ) . path ( ) ) ;
2024-05-18 03:08:36 +03:00
if ( ! files . isEmpty ( ) ) {
dialog . data ( ) - > selectionModeOption ( ) ;
}
2014-11-18 17:46:34 +00:00
if ( dialog . data ( ) - > exec ( ) ) {
//this is done to update the quick extract menu
updateActions ( ) ;
Kerfuffle : : ExtractionOptions options ;
if ( dialog . data ( ) - > preservePaths ( ) ) {
options [ QLatin1String ( " PreservePaths " ) ] = true ;
}
options [ QLatin1String ( " FollowExtractionDialogSettings " ) ] = true ;
2024-05-18 03:08:36 +03:00
QString destinationDirectory = dialog . data ( ) - > destinationDirectory ( ) ;
2024-05-18 23:51:23 +03:00
if ( dialog . data ( ) - > autoSubfolders ( ) & & ! isSingleFolderArchive ( ) ) {
2024-05-18 03:08:36 +03:00
QString subFolder = detectSubfolder ( ) ;
if ( ! subFolder . isEmpty ( ) ) {
// do what batch extraction does, create the folder or automatically pick other if it
// exists
const QDir destDir ( destinationDirectory ) ;
if ( destDir . exists ( subFolder ) ) {
subFolder = KIO : : RenameDialog : : suggestName ( destinationDirectory , subFolder ) ;
}
destDir . mkdir ( subFolder ) ;
if ( ! destinationDirectory . endsWith ( QDir : : separator ( ) ) ) {
destinationDirectory + = QDir : : separator ( ) ;
}
destinationDirectory + = subFolder ;
}
}
kDebug ( ) < < " Destination " < < destinationDirectory ;
2014-11-18 17:46:34 +00:00
ExtractJob * job = m_model - > extractFiles ( files , destinationDirectory , options ) ;
registerJob ( job ) ;
connect ( job , SIGNAL ( result ( KJob * ) ) ,
this , SLOT ( slotExtractionDone ( KJob * ) ) ) ;
job - > start ( ) ;
}
delete dialog . data ( ) ;
}
QList < QVariant > Part : : selectedFilesWithChildren ( )
{
Q_ASSERT ( m_model ) ;
QModelIndexList toIterate = m_view - > selectionModel ( ) - > selectedRows ( ) ;
for ( int i = 0 ; i < toIterate . size ( ) ; + + i ) {
QModelIndex index = toIterate . at ( i ) ;
for ( int j = 0 ; j < m_model - > rowCount ( index ) ; + + j ) {
QModelIndex child = m_model - > index ( j , 0 , index ) ;
if ( ! toIterate . contains ( child ) ) {
toIterate < < child ;
}
}
}
QVariantList ret ;
foreach ( const QModelIndex & index , toIterate ) {
const ArchiveEntry & entry = m_model - > entryForIndex ( index ) ;
if ( entry . contains ( InternalID ) ) {
ret < < entry [ InternalID ] ;
}
}
return ret ;
}
QList < QVariant > Part : : selectedFiles ( )
{
QStringList toSort ;
foreach ( const QModelIndex & index , m_view - > selectionModel ( ) - > selectedRows ( ) ) {
const ArchiveEntry & entry = m_model - > entryForIndex ( index ) ;
toSort < < entry [ InternalID ] . toString ( ) ;
}
toSort . sort ( ) ;
QVariantList ret ;
foreach ( const QString & i , toSort ) {
ret < < i ;
}
return ret ;
}
void Part : : slotExtractionDone ( KJob * job )
{
2024-05-16 03:34:03 +03:00
if ( job - > error ( ) ! = KJob : : NoError ) {
2014-11-18 17:46:34 +00:00
KMessageBox : : error ( widget ( ) , job - > errorString ( ) ) ;
} else {
ExtractJob * extractJob = qobject_cast < ExtractJob * > ( job ) ;
Q_ASSERT ( extractJob ) ;
const bool followExtractionDialogSettings =
extractJob - > extractionOptions ( ) . value ( QLatin1String ( " FollowExtractionDialogSettings " ) , false ) . toBool ( ) ;
if ( ! followExtractionDialogSettings ) {
return ;
}
if ( ArkSettings : : openDestinationFolderAfterExtraction ( ) ) {
2024-05-12 03:22:20 +03:00
KToolInvocation : : self ( ) - > startServiceForUrl ( extractJob - > destinationDirectory ( ) , widget ( ) ) ;
2014-11-18 17:46:34 +00:00
}
if ( ArkSettings : : closeAfterExtraction ( ) ) {
emit quit ( ) ;
}
}
}
void Part : : adjustColumns ( )
{
m_view - > header ( ) - > setResizeMode ( 0 , QHeaderView : : ResizeToContents ) ;
}
void Part : : slotAddFiles ( const QStringList & filesToAdd , const QString & path )
{
if ( filesToAdd . isEmpty ( ) ) {
return ;
}
kDebug ( ) < < " Adding " < < filesToAdd < < " to " < < path ;
kDebug ( ) < < " Warning, for now the path argument is not implemented " ;
QStringList cleanFilesToAdd ( filesToAdd ) ;
for ( int i = 0 ; i < cleanFilesToAdd . size ( ) ; + + i ) {
QString & file = cleanFilesToAdd [ i ] ;
if ( QFileInfo ( file ) . isDir ( ) ) {
if ( ! file . endsWith ( QLatin1Char ( ' / ' ) ) ) {
file + = QLatin1Char ( ' / ' ) ;
}
}
}
CompressionOptions options ;
QString firstPath = cleanFilesToAdd . first ( ) ;
if ( firstPath . right ( 1 ) = = QLatin1String ( " / " ) ) {
firstPath . chop ( 1 ) ;
}
firstPath = QFileInfo ( firstPath ) . dir ( ) . absolutePath ( ) ;
kDebug ( ) < < " Detected relative path to be " < < firstPath ;
options [ QLatin1String ( " GlobalWorkDir " ) ] = firstPath ;
AddJob * job = m_model - > addFiles ( cleanFilesToAdd , options ) ;
if ( ! job ) {
return ;
}
2024-05-16 04:36:33 +03:00
connect ( job , SIGNAL ( result ( KJob * ) ) , this , SLOT ( slotAddFilesDone ( KJob * ) ) ) ;
2014-11-18 17:46:34 +00:00
registerJob ( job ) ;
job - > start ( ) ;
}
void Part : : slotAddFiles ( )
{
// #264819: passing widget() as the parent will not work as expected.
// KFileDialog will create a KFileWidget, which runs an internal
// event loop to stat the given directory. This, in turn, leads to
// events being delivered to widget(), which is a QSplitter, which
// in turn reimplements childEvent() and will end up calling
// QWidget::show() on the KFileDialog (thus showing it in a
// non-modal state).
// When KFileDialog::exec() is called, the widget is already shown
// and nothing happens.
2024-05-16 03:34:03 +03:00
const QStringList filesToAdd = KFileDialog : : getOpenFileNames (
KUrl ( " kfiledialog:///ArkAddFiles " ) ,
QString ( ) , widget ( ) - > parentWidget ( ) ,
i18nc ( " @title:window " , " Add Files " )
) ;
2014-11-18 17:46:34 +00:00
slotAddFiles ( filesToAdd ) ;
}
void Part : : slotAddDir ( )
{
2024-05-16 03:34:03 +03:00
const QString dirToAdd = KFileDialog : : getExistingDirectory (
KUrl ( " kfiledialog:///ArkAddFiles " ) ,
widget ( ) ,
i18nc ( " @title:window " , " Add Folder " )
) ;
2014-11-18 17:46:34 +00:00
if ( ! dirToAdd . isEmpty ( ) ) {
slotAddFiles ( QStringList ( ) < < dirToAdd ) ;
}
}
void Part : : slotAddFilesDone ( KJob * job )
{
2024-05-16 03:34:03 +03:00
if ( job - > error ( ) ! = KJob : : NoError ) {
2014-11-18 17:46:34 +00:00
KMessageBox : : error ( widget ( ) , job - > errorString ( ) ) ;
2024-05-16 04:36:33 +03:00
} else {
setModified ( ) ;
2014-11-18 17:46:34 +00:00
}
}
void Part : : slotDeleteFilesDone ( KJob * job )
{
2024-05-16 03:34:03 +03:00
if ( job - > error ( ) ! = KJob : : NoError ) {
2014-11-18 17:46:34 +00:00
KMessageBox : : error ( widget ( ) , job - > errorString ( ) ) ;
2024-05-16 04:36:33 +03:00
} else {
setModified ( ) ;
2014-11-18 17:46:34 +00:00
}
}
void Part : : slotDeleteFiles ( )
{
2024-05-16 03:34:03 +03:00
const int reallyDelete = KMessageBox : : questionYesNo (
NULL ,
i18n ( " Deleting these files is not undoable. Are you sure you want to do this? " ) ,
i18nc ( " @title:window " , " Delete files " ) ,
KStandardGuiItem : : del ( ) ,
KStandardGuiItem : : cancel ( ) ,
QString ( ) ,
KMessageBox : : Dangerous | KMessageBox : : Notify
) ;
2014-11-18 17:46:34 +00:00
if ( reallyDelete = = KMessageBox : : No ) {
return ;
}
DeleteJob * job = m_model - > deleteFiles ( selectedFilesWithChildren ( ) ) ;
2024-05-16 04:36:33 +03:00
connect ( job , SIGNAL ( result ( KJob * ) ) , this , SLOT ( slotDeleteFilesDone ( KJob * ) ) ) ;
2014-11-18 17:46:34 +00:00
registerJob ( job ) ;
job - > start ( ) ;
}
void Part : : slotToggleInfoPanel ( bool visible )
{
QList < int > splitterSizes ;
if ( visible ) {
splitterSizes = ArkSettings : : splitterSizesWithBothWidgets ( ) ;
} else {
splitterSizes = m_splitter - > sizes ( ) ;
ArkSettings : : setSplitterSizesWithBothWidgets ( splitterSizes ) ;
splitterSizes [ 1 ] = 0 ;
}
m_splitter - > setSizes ( splitterSizes ) ;
saveSplitterSizes ( ) ;
}
void Part : : saveSplitterSizes ( )
{
ArkSettings : : setSplitterSizes ( m_splitter - > sizes ( ) ) ;
ArkSettings : : self ( ) - > writeConfig ( ) ;
}
void Part : : slotSaveAs ( )
{
2024-05-16 04:36:33 +03:00
KUrl saveUrl = KFileDialog : : getSaveUrl (
KUrl ( QLatin1String ( " kfiledialog:///ArkSaveAs/ " ) + url ( ) . fileName ( ) ) ,
QString ( ) ,
widget ( )
) ;
2014-11-18 17:46:34 +00:00
if ( ( saveUrl . isValid ( ) ) & & ( ! saveUrl . isEmpty ( ) ) ) {
if ( KIO : : NetAccess : : exists ( saveUrl , KIO : : NetAccess : : DestinationSide , widget ( ) ) ) {
2024-05-16 04:36:33 +03:00
int overwrite = KMessageBox : : warningContinueCancel (
widget ( ) ,
i18nc ( " @info " , " An archive named <tt>%1</tt> already exists. Are you sure you want to overwrite it? " , saveUrl . fileName ( ) ) ,
QString ( ) ,
KStandardGuiItem : : overwrite ( )
) ;
2014-11-18 17:46:34 +00:00
if ( overwrite ! = KMessageBox : : Continue ) {
return ;
}
}
KUrl srcUrl = KUrl : : fromPath ( localFilePath ( ) ) ;
if ( ! QFile : : exists ( localFilePath ( ) ) ) {
if ( url ( ) . isLocalFile ( ) ) {
2024-05-16 04:36:33 +03:00
KMessageBox : : error (
widget ( ) ,
i18nc ( " @info " , " The archive <tt>%1</tt> cannot be copied to the specified location. The archive does not exist anymore. " , localFilePath ( ) )
) ;
2014-11-18 17:46:34 +00:00
return ;
} else {
srcUrl = url ( ) ;
}
}
KIO : : Job * copyJob = KIO : : file_copy ( srcUrl , saveUrl , - 1 , KIO : : Overwrite ) ;
if ( ! KIO : : NetAccess : : synchronousRun ( copyJob , widget ( ) ) ) {
2024-05-16 04:36:33 +03:00
KMessageBox : : error (
widget ( ) ,
i18nc ( " @info " , " The archive could not be saved as <tt>%1</tt>. Try saving it to another location. " , saveUrl . pathOrUrl ( ) )
) ;
2014-11-18 17:46:34 +00:00
}
}
}
2014-12-21 12:48:25 +00:00
void Part : : slotShowContextMenu ( )
{
if ( ! factory ( ) ) {
return ;
}
KMenu * popup = static_cast < KMenu * > ( factory ( ) - > container ( QLatin1String ( " context_menu " ) , this ) ) ;
popup - > popup ( QCursor : : pos ( ) ) ;
}
2014-11-18 17:46:34 +00:00
} // namespace Ark