File indexing completed on 2024-05-05 04:51:45

0001 /*
0002     SPDX-FileCopyrightText: 2003-2008 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 2009 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
0004     SPDX-FileCopyrightText: 2009-2011 Michal Malek <michalm@jabster.pl>
0005     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "k3bdataviewimpl.h"
0011 #include "k3bbootimagedialog.h"
0012 #include "k3bdatadoc.h"
0013 #include "k3bdatamultisessionimportdialog.h"
0014 #include "k3bdataprojectdelegate.h"
0015 #include "k3bdataprojectmodel.h"
0016 #include "k3bdataprojectsortproxymodel.h"
0017 #include "k3bdatapropertiesdialog.h"
0018 #include "k3bdataurladdingdialog.h"
0019 #include "k3bdiritem.h"
0020 #include "k3bview.h"
0021 #include "k3bviewcolumnadjuster.h"
0022 #include "k3bvolumenamewidget.h"
0023 
0024 #include <KLocalizedString>
0025 #include <KFileItemDelegate>
0026 #include <KActionCollection>
0027 #include <KIO/JobUiDelegate>
0028 #include <KIO/OpenUrlJob>
0029 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0030 #include <KIO/JobUiDelegateFactory>
0031 #endif
0032 
0033 #include <QSortFilterProxyModel>
0034 #include <QAction>
0035 #include <QDialog>
0036 #include <QDialogButtonBox>
0037 #include <QInputDialog>
0038 #include <QShortcut>
0039 #include <QWidgetAction>
0040 
0041 
0042 namespace {
0043 
0044 class VolumeNameWidgetAction : public QWidgetAction
0045 {
0046 public:
0047     VolumeNameWidgetAction( K3b::DataDoc* doc, QObject* parent )
0048     :
0049         QWidgetAction( parent ),
0050         m_doc( doc )
0051     {
0052     }
0053 
0054 protected:
0055     QWidget* createWidget( QWidget* parent ) override
0056     {
0057         return new K3b::VolumeNameWidget( m_doc, parent );
0058     }
0059 
0060 private:
0061     K3b::DataDoc* m_doc;
0062 };
0063 
0064 } // namespace
0065 
0066 
0067 K3b::DataViewImpl::DataViewImpl( View* view, DataDoc* doc, KActionCollection* actionCollection )
0068 :
0069     QObject( view ),
0070     m_view( view ),
0071     m_doc( doc ),
0072     m_model( new DataProjectModel( doc, view ) ),
0073     m_sortModel( new DataProjectSortProxyModel( this ) ),
0074     m_fileView( new QTreeView( view ) )
0075 {
0076     connect( m_doc, SIGNAL(importedSessionChanged(int)), this, SLOT(slotImportedSessionChanged(int)) );
0077     connect( m_model, SIGNAL(addUrlsRequested(QList<QUrl>,K3b::DirItem*)), SLOT(slotAddUrlsRequested(QList<QUrl>,K3b::DirItem*)) );
0078     connect( m_model, SIGNAL(moveItemsRequested(QList<K3b::DataItem*>,K3b::DirItem*)), SLOT(slotMoveItemsRequested(QList<K3b::DataItem*>,K3b::DirItem*)) );
0079 
0080     m_sortModel->setSourceModel( m_model );
0081 
0082     m_fileView->setItemDelegate( new DataProjectDelegate( this ) );
0083     m_fileView->setModel( m_sortModel );
0084     m_fileView->setAcceptDrops( true );
0085     m_fileView->setDragEnabled( true );
0086     m_fileView->setDragDropMode( QTreeView::DragDrop );
0087     m_fileView->setItemsExpandable( false );
0088     m_fileView->setRootIsDecorated( false );
0089     m_fileView->setSelectionMode( QTreeView::ExtendedSelection );
0090     m_fileView->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
0091     m_fileView->setContextMenuPolicy( Qt::ActionsContextMenu );
0092     m_fileView->setSortingEnabled( true );
0093     m_fileView->sortByColumn( DataProjectModel::FilenameColumn, Qt::AscendingOrder );
0094     m_fileView->setMouseTracking( true );
0095     m_fileView->setAllColumnsShowFocus( true );
0096     connect( m_fileView, SIGNAL(doubleClicked(QModelIndex)),
0097              this, SLOT(slotItemActivated(QModelIndex)) );
0098     connect( m_fileView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
0099              this, SLOT(slotSelectionChanged()) );
0100 
0101     m_columnAdjuster = new ViewColumnAdjuster( this );
0102     m_columnAdjuster->setView( m_fileView );
0103     m_columnAdjuster->addFixedColumn( DataProjectModel::TypeColumn );
0104     m_columnAdjuster->setColumnMargin( DataProjectModel::TypeColumn, 10 );
0105     m_columnAdjuster->addFixedColumn( DataProjectModel::SizeColumn );
0106     m_columnAdjuster->setColumnMargin( DataProjectModel::SizeColumn, 10 );
0107 
0108     m_actionNewDir = new QAction( QIcon::fromTheme( "folder-new" ), i18n("New Folder..."), m_fileView );
0109     m_actionNewDir->setShortcut( Qt::CTRL | Qt::Key_N );
0110     m_actionNewDir->setShortcutContext( Qt::WidgetShortcut );
0111     actionCollection->addAction( "new_dir", m_actionNewDir );
0112     connect( m_actionNewDir, SIGNAL(triggered(bool)), this, SLOT(slotNewDir()) );
0113 
0114     m_actionRemove = new QAction( QIcon::fromTheme( "edit-delete" ), i18n("Remove"), m_fileView );
0115     m_actionRemove->setShortcut( Qt::Key_Delete );
0116     m_actionRemove->setShortcutContext( Qt::WidgetShortcut );
0117     actionCollection->addAction( "remove", m_actionRemove );
0118     connect( m_actionRemove, SIGNAL(triggered(bool)), this, SLOT(slotRemove()) );
0119 
0120     m_actionRename = new QAction( QIcon::fromTheme( "edit-rename" ), i18n("Rename"), m_fileView );
0121     m_actionRename->setShortcut( Qt::Key_F2 );
0122     m_actionRename->setShortcutContext( Qt::WidgetShortcut );
0123     actionCollection->addAction( "rename", m_actionRename );
0124     connect( m_actionRename, SIGNAL(triggered(bool)), this, SLOT(slotRename()) );
0125 
0126     m_actionParentDir = new QAction( QIcon::fromTheme( "go-up" ), i18n("Parent Folder"), m_fileView );
0127     m_actionParentDir->setShortcut( Qt::Key_Backspace );
0128     m_actionParentDir->setShortcutContext( Qt::WidgetShortcut );
0129     actionCollection->addAction( "parent_dir", m_actionParentDir );
0130 
0131     m_actionProperties = new QAction( QIcon::fromTheme( "document-properties" ), i18n("Properties"), m_fileView );
0132     m_actionProperties->setShortcut( Qt::ALT | Qt::Key_Return );
0133     m_actionProperties->setShortcutContext( Qt::WidgetShortcut );
0134     actionCollection->addAction( "properties", m_actionProperties );
0135     connect( m_actionProperties, SIGNAL(triggered(bool)), this, SLOT(slotProperties()) );
0136 
0137     m_actionOpen = new QAction( QIcon::fromTheme( "document-open" ), i18n("Open"), m_view );
0138     actionCollection->addAction( "open", m_actionOpen );
0139     connect( m_actionOpen, SIGNAL(triggered(bool)), this, SLOT(slotOpen()) );
0140 
0141     m_actionImportSession = new QAction( QIcon::fromTheme( "document-import" ), i18n("&Import Session..."), m_view );
0142     m_actionImportSession->setToolTip( i18n("Import a previously burned session into the current project") );
0143     actionCollection->addAction( "project_data_import_session", m_actionImportSession );
0144     connect( m_actionImportSession, SIGNAL(triggered(bool)), this, SLOT(slotImportSession()) );
0145 
0146     m_actionClearSession = new QAction( QIcon::fromTheme( "edit-clear" ), i18n("&Clear Imported Session"), m_view );
0147     m_actionClearSession->setToolTip( i18n("Remove the imported items from a previous session") );
0148     m_actionClearSession->setEnabled( m_doc->importedSession() > -1 );
0149     actionCollection->addAction( "project_data_clear_imported_session", m_actionClearSession );
0150     connect( m_actionClearSession, SIGNAL(triggered(bool)), this, SLOT(slotClearImportedSession()) );
0151 
0152     m_actionEditBootImages = new QAction( QIcon::fromTheme( "document-properties" ), i18n("&Edit Boot Images..."), m_view );
0153     m_actionEditBootImages->setToolTip( i18n("Modify the bootable settings of the current project") );
0154     actionCollection->addAction( "project_data_edit_boot_images", m_actionEditBootImages );
0155     connect( m_actionEditBootImages, SIGNAL(triggered(bool)), this, SLOT(slotEditBootImages()) );
0156 
0157     QWidgetAction* volumeNameWidgetAction = new VolumeNameWidgetAction( m_doc, this );
0158     actionCollection->addAction( "project_volume_name", volumeNameWidgetAction );
0159 
0160     QShortcut* enterShortcut = new QShortcut( QKeySequence( Qt::Key_Return ), m_fileView );
0161     enterShortcut->setContext( Qt::WidgetShortcut );
0162     connect( enterShortcut, SIGNAL(activated()), this, SLOT(slotEnterPressed()) );
0163 
0164     // Create data context menu
0165     QAction* separator = new QAction( this );
0166     separator->setSeparator( true );
0167     m_fileView->addAction( m_actionParentDir );
0168     m_fileView->addAction( separator );
0169     m_fileView->addAction( m_actionRename );
0170     m_fileView->addAction( m_actionRemove );
0171     m_fileView->addAction( m_actionNewDir );
0172     m_fileView->addAction( separator );
0173     m_fileView->addAction( m_actionOpen );
0174     m_fileView->addAction( separator );
0175     m_fileView->addAction( m_actionProperties );
0176     m_fileView->addAction( separator );
0177     m_fileView->addAction( actionCollection->action("project_burn") );
0178 }
0179 
0180 
0181 void K3b::DataViewImpl::addUrls( const QModelIndex& parent, const QList<QUrl>& urls )
0182 {
0183     DirItem *item = dynamic_cast<DirItem*>( m_model->itemForIndex( parent ) );
0184     if (!item)
0185         item = m_doc->root();
0186 
0187     DataUrlAddingDialog::addUrls( urls, item, m_view );
0188 }
0189 
0190 
0191 void K3b::DataViewImpl::slotCurrentRootChanged( const QModelIndex& newRoot )
0192 {
0193     // make the file view show only the child nodes of the currently selected
0194     // directory from dir view
0195     m_fileView->setRootIndex( m_sortModel->mapFromSource( newRoot ) );
0196     m_columnAdjuster->adjustColumns();
0197     m_actionParentDir->setEnabled( newRoot.isValid() && m_model->indexForItem( m_doc->root() ) != newRoot );
0198 }
0199 
0200 
0201 void K3b::DataViewImpl::slotNewDir()
0202 {
0203     const QModelIndex parent = m_sortModel->mapToSource( m_fileView->rootIndex() );
0204     DirItem* parentDir = 0;
0205 
0206     if (parent.isValid())
0207         parentDir = dynamic_cast<DirItem*>( m_model->itemForIndex( parent ) );
0208 
0209     if (!parentDir)
0210         parentDir = m_doc->root();
0211 
0212     QString name;
0213     bool ok;
0214 
0215     name = QInputDialog::getText( m_view,
0216                                   i18n("New Folder"),
0217                                   i18n("Please insert the name for the new folder:"),
0218                                   QLineEdit::Normal,
0219                                   i18n("New Folder"),
0220                                   &ok );
0221 
0222     while( ok && DataDoc::nameAlreadyInDir( name, parentDir ) ) {
0223         name = QInputDialog::getText( m_view,
0224                                       i18n("New Folder"),
0225                                       i18n("A file with that name already exists. "
0226                                            "Please insert the name for the new folder:"),
0227                                       QLineEdit::Normal,
0228                                       i18n("New Folder"),
0229                                       &ok );
0230     }
0231 
0232     if( !ok )
0233         return;
0234 
0235     m_doc->addEmptyDir( name, parentDir );
0236 }
0237 
0238 
0239 void K3b::DataViewImpl::slotRemove()
0240 {
0241     // Remove items directly from sort model to avoid unnecessary mapping of indexes
0242     const QItemSelection selection = m_fileView->selectionModel()->selection();
0243     const QModelIndex parentDirectory = m_fileView->rootIndex();
0244     for( int i = selection.size() - 1; i >= 0; --i ) {
0245         m_sortModel->removeRows( selection.at(i).top(), selection.at(i).height(), parentDirectory );
0246     }
0247 }
0248 
0249 
0250 void K3b::DataViewImpl::slotRename()
0251 {
0252     const QModelIndex current = m_fileView->currentIndex();
0253     if( current.isValid() ) {
0254         m_fileView->edit( current );
0255     }
0256 }
0257 
0258 
0259 void K3b::DataViewImpl::slotProperties()
0260 {
0261     const QModelIndexList indices = m_fileView->selectionModel()->selectedRows();
0262     if ( indices.isEmpty() )
0263     {
0264         // show project properties
0265         m_view->slotProperties();
0266     }
0267     else
0268     {
0269         QList<DataItem*> items;
0270 
0271         foreach(const QModelIndex& index, indices) {
0272             items.append( m_model->itemForIndex( m_sortModel->mapToSource( index ) ) );
0273         }
0274 
0275         DataPropertiesDialog dlg( items, m_view );
0276         dlg.exec();
0277     }
0278 }
0279 
0280 
0281 void K3b::DataViewImpl::slotOpen()
0282 {
0283     const QModelIndex current = m_sortModel->mapToSource( m_fileView->currentIndex() );
0284     if( !current.isValid() )
0285         return;
0286 
0287     DataItem* item = m_model->itemForIndex( current );
0288 
0289     if( !item->isFile() ) {
0290         QUrl url = QUrl::fromLocalFile( item->localPath() );
0291         auto *job = new KIO::OpenUrlJob( url, item->mimeType().name() );
0292         job->setRunExecutables( false ); // this is the default, but let's make really sure :)
0293 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0294         auto uiDelegate = new KIO::JobUiDelegate( KJobUiDelegate::AutoHandlingEnabled, m_view );
0295 #else
0296         auto uiDelegate = KIO::createDefaultJobUiDelegate( KJobUiDelegate::AutoHandlingEnabled, m_view );
0297 #endif
0298         job->setUiDelegate( uiDelegate );
0299         job->start();
0300     }
0301 }
0302 
0303 
0304 void K3b::DataViewImpl::slotSelectionChanged()
0305 {
0306     const QModelIndexList indexes = m_fileView->selectionModel()->selectedRows();
0307 
0308     bool open = true, rename = true, remove = true;
0309 
0310     // we can only rename one item at a time
0311     // also, we can only create a new dir over a single directory
0312     if (indexes.count() > 1)
0313     {
0314         rename = false;
0315         open = false;
0316     }
0317     else if (indexes.count() == 1)
0318     {
0319         QModelIndex index = indexes.first();
0320         rename = (index.flags() & Qt::ItemIsEditable);
0321         open = (index.data(DataProjectModel::ItemTypeRole).toInt() == DataProjectModel::FileItemType);
0322     }
0323     else // selectedIndex.count() == 0
0324     {
0325         remove = false;
0326         rename = false;
0327         open = false;
0328     }
0329 
0330     // check if all selected items can be removed
0331     foreach(const QModelIndex &index, indexes)
0332     {
0333         if (!(index.data(DataProjectModel::CustomFlagsRole).toInt() & DataProjectModel::ItemIsRemovable))
0334         {
0335             remove = false;
0336             break;
0337         }
0338     }
0339 
0340     m_actionRename->setEnabled( rename );
0341     m_actionRemove->setEnabled( remove );
0342     m_actionOpen->setEnabled( open );
0343 }
0344 
0345 
0346 void K3b::DataViewImpl::slotItemActivated( const QModelIndex& index )
0347 {
0348     if( index.isValid() ) {
0349         const int type = index.data( DataProjectModel::ItemTypeRole ).toInt();
0350         if( type == DataProjectModel::DirItemType ) {
0351             emit setCurrentRoot( m_sortModel->mapToSource( index ) );
0352         }
0353         else if( type == DataProjectModel::FileItemType ) {
0354             m_fileView->edit( index );
0355         }
0356     }
0357 }
0358 
0359 
0360 void K3b::DataViewImpl::slotEnterPressed()
0361 {
0362     slotItemActivated( m_fileView->currentIndex() );
0363 }
0364 
0365 
0366 void K3b::DataViewImpl::slotImportSession()
0367 {
0368     K3b::DataMultisessionImportDialog::importSession( m_doc, m_view );
0369 }
0370 
0371 
0372 void K3b::DataViewImpl::slotClearImportedSession()
0373 {
0374     m_doc->clearImportedSession();
0375 }
0376 
0377 
0378 void K3b::DataViewImpl::slotEditBootImages()
0379 {
0380     BootImageDialog dlg( m_doc );
0381     dlg.setWindowTitle( i18n("Edit Boot Images") );
0382     dlg.exec();
0383 }
0384 
0385 
0386 void K3b::DataViewImpl::slotImportedSessionChanged( int importedSession )
0387 {
0388     m_actionClearSession->setEnabled( importedSession > -1 );
0389 }
0390 
0391 
0392 void K3b::DataViewImpl::slotAddUrlsRequested( QList<QUrl> urls, K3b::DirItem* targetDir )
0393 {
0394     DataUrlAddingDialog::addUrls( urls, targetDir, m_view );
0395 }
0396 
0397 
0398 void K3b::DataViewImpl::slotMoveItemsRequested( QList<K3b::DataItem*> items, K3b::DirItem* targetDir )
0399 {
0400     DataUrlAddingDialog::moveItems( items, targetDir, m_view );
0401 }
0402 
0403 #include "moc_k3bdataviewimpl.cpp"