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"