Skip to content

Commit 7a4fc6e

Browse files
committedAug 10, 2021
[feature] Add Rename action under the manage submenu for files in browser
Allows renaming of files directly in the browser. If the file corresponds to a spatial dataset with multiple sidecar files then these will all be renamed accordingly too. Additionally, users are warned if the file is a layer which is exists in the current project and are asked whether they want to automatically update all the layer paths accordingly. Refs #14611
1 parent 1608547 commit 7a4fc6e

File tree

1 file changed

+97
-9
lines changed

1 file changed

+97
-9
lines changed
 

‎src/app/browser/qgsinbuiltdataitemproviders.cpp

Lines changed: 97 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -503,31 +503,119 @@ void QgsAppFileItemGuiProvider::populateContextMenu( QgsDataItem *item, QMenu *m
503503

504504
QMenu *manageFileMenu = new QMenu( tr( "Manage" ), menu );
505505

506-
QStringList selectedDeletableFiles;
506+
QStringList selectedManagableFiles;
507507
QList< QPointer< QgsDataItem > > selectedParents;
508508
for ( QgsDataItem *selectedItem : selectedItems )
509509
{
510510
if ( selectedItem->capabilities2() & Qgis::BrowserItemCapability::ItemRepresentsFile )
511511
{
512-
selectedDeletableFiles.append( selectedItem->path() );
512+
selectedManagableFiles.append( selectedItem->path() );
513513
selectedParents << selectedItem->parent();
514514
}
515515
}
516-
const QString deleteText = selectedDeletableFiles.count() == 1 ? tr( "Delete “%1”…" ).arg( fi.fileName() )
516+
517+
if ( selectedManagableFiles.size() == 1 )
518+
{
519+
const QString renameText = tr( "Rename “%1”…" ).arg( fi.fileName() );
520+
QAction *renameAction = new QAction( renameText, menu );
521+
connect( renameAction, &QAction::triggered, this, [ = ]
522+
{
523+
const QString oldPath = selectedManagableFiles.value( 0 );
524+
// Check if the file corresponds to paths in the project
525+
const QList<QgsMapLayer *> layersList = QgsProjectUtils::layersMatchingPath( QgsProject::instance(), oldPath );
526+
527+
const QStringList existingNames = QFileInfo( oldPath ).dir().entryList();
528+
529+
QgsNewNameDialog dlg( tr( "file" ), QFileInfo( oldPath ).fileName(), QStringList(), existingNames, Qt::CaseInsensitive, menu );
530+
dlg.setWindowTitle( tr( "Rename %1" ).arg( QFileInfo( oldPath ).fileName() ) );
531+
dlg.setHintString( tr( "Rename “%1” to" ).arg( QFileInfo( oldPath ).fileName() ) );
532+
dlg.setOverwriteEnabled( false );
533+
dlg.setConflictingNameWarning( tr( "A file with this name already exists." ) );
534+
if ( dlg.exec() != QDialog::Accepted || dlg.name().isEmpty() )
535+
return;
536+
537+
QString newName = dlg.name();
538+
if ( QFileInfo( newName ).suffix().isEmpty() )
539+
newName = newName + '.' + QFileInfo( oldPath ).suffix();
540+
541+
const QString newPath = QFileInfo( oldPath ).dir().filePath( newName );
542+
543+
bool updateLayers = false;
544+
if ( !layersList.empty() )
545+
{
546+
QMessageBox message( QMessageBox::Warning, tr( "Rename %1" ).arg( QFileInfo( oldPath ).fileName() ),
547+
tr( "The file %1 exists in the current project. Are you sure you want to rename it?" ).arg( QFileInfo( oldPath ).fileName() ),
548+
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
549+
message.setDefaultButton( QMessageBox::Cancel );
550+
message.setButtonText( QMessageBox::Yes, tr( "Rename and Update Layer Paths" ) );
551+
message.setButtonText( QMessageBox::No, tr( "Rename but Leave Layer Paths" ) );
552+
553+
QStringList layerNames;
554+
layerNames.reserve( layersList.size() );
555+
for ( const QgsMapLayer *layer : std::as_const( layersList ) )
556+
{
557+
layerNames << layer->name();
558+
}
559+
const QString detailedText = tr( "The following layers will be affected:" ) + QStringLiteral( "\n\n• %1" ).arg( layerNames.join( QStringLiteral( "\n" ) ) );
560+
message.setDetailedText( detailedText );
561+
562+
int res = message.exec();
563+
if ( res == QMessageBox::Cancel )
564+
return;
565+
566+
if ( res == QMessageBox::Yes )
567+
updateLayers = true;
568+
}
569+
570+
QString error;
571+
const bool result = QgsFileUtils::renameDataset( oldPath, newPath, error );
572+
573+
for ( const QPointer< QgsDataItem > &parent : selectedParents )
574+
{
575+
if ( parent )
576+
parent->refresh();
577+
}
578+
579+
if ( !layersList.empty() )
580+
{
581+
if ( updateLayers )
582+
{
583+
QgsProjectUtils::updateLayerPath( QgsProject::instance(), oldPath, newPath );
584+
QgsProject::instance()->setDirty( true );
585+
}
586+
else
587+
{
588+
// we just update the layer source to get it to recognize that it's now broken in the UI
589+
for ( QgsMapLayer *layer : std::as_const( layersList ) )
590+
{
591+
layer->setDataSource( layer->source(), layer->name(), layer->providerType() );
592+
}
593+
}
594+
}
595+
596+
if ( !result )
597+
{
598+
notify( QString(), error, context, Qgis::MessageLevel::Critical );
599+
}
600+
} );
601+
manageFileMenu->addAction( renameAction );
602+
}
603+
604+
const QString deleteText = selectedManagableFiles.count() == 1 ? tr( "Delete “%1”…" ).arg( fi.fileName() )
517605
: tr( "Delete Selected Files…" );
518606
QAction *deleteAction = new QAction( deleteText, menu );
519607
connect( deleteAction, &QAction::triggered, this, [ = ]
520608
{
521609
// Check if the files correspond to paths in the project
522610
QList<QgsMapLayer *> layersList;
523-
for ( const QString &path : std::as_const( selectedDeletableFiles ) )
611+
for ( const QString &path : std::as_const( selectedManagableFiles ) )
524612
{
525613
layersList << QgsProjectUtils::layersMatchingPath( QgsProject::instance(), path );
526614
}
527615

528616
// now expand out the list of files to include all sidecar files (e.g. .aux.xml files)
529617
QSet< QString > allFilesWithSidecars;
530-
for ( const QString &file : std::as_const( selectedDeletableFiles ) )
618+
for ( const QString &file : std::as_const( selectedManagableFiles ) )
531619
{
532620
allFilesWithSidecars.insert( file );
533621
allFilesWithSidecars.unite( QgsFileUtils::sidecarFilesForPath( file ) );
@@ -542,9 +630,9 @@ void QgsAppFileItemGuiProvider::populateContextMenu( QgsDataItem *item, QMenu *m
542630
if ( layersList.empty() )
543631
{
544632
// generic warning
545-
QMessageBox message( QMessageBox::Warning, sortedAllFilesWithSidecars.size() > 1 ? tr( "Delete Files" ) : tr( "Delete %1" ).arg( QFileInfo( selectedDeletableFiles.at( 0 ) ).fileName() ),
633+
QMessageBox message( QMessageBox::Warning, sortedAllFilesWithSidecars.size() > 1 ? tr( "Delete Files" ) : tr( "Delete %1" ).arg( QFileInfo( selectedManagableFiles.at( 0 ) ).fileName() ),
546634
sortedAllFilesWithSidecars.size() > 1 ? tr( "Permanently delete %1 files?" ).arg( sortedAllFilesWithSidecars.size() )
547-
: tr( "Permanently delete “%1”?" ).arg( QFileInfo( selectedDeletableFiles.at( 0 ) ).fileName() ),
635+
: tr( "Permanently delete “%1”?" ).arg( QFileInfo( selectedManagableFiles.at( 0 ) ).fileName() ),
548636
QMessageBox::Yes | QMessageBox::No );
549637
message.setDefaultButton( QMessageBox::No );
550638

@@ -565,9 +653,9 @@ void QgsAppFileItemGuiProvider::populateContextMenu( QgsDataItem *item, QMenu *m
565653
}
566654
else
567655
{
568-
QMessageBox message( QMessageBox::Warning, sortedAllFilesWithSidecars.size() > 1 ? tr( "Delete Files" ) : tr( "Delete %1" ).arg( QFileInfo( selectedDeletableFiles.at( 0 ) ).fileName() ),
656+
QMessageBox message( QMessageBox::Warning, sortedAllFilesWithSidecars.size() > 1 ? tr( "Delete Files" ) : tr( "Delete %1" ).arg( QFileInfo( selectedManagableFiles.at( 0 ) ).fileName() ),
569657
sortedAllFilesWithSidecars.size() > 1 ? tr( "One or more selected files exist in the current project. Are you sure you want to delete these files?" )
570-
: tr( "The file %1 exists in the current project. Are you sure you want to delete it?" ).arg( QFileInfo( selectedDeletableFiles.at( 0 ) ).fileName() ),
658+
: tr( "The file %1 exists in the current project. Are you sure you want to delete it?" ).arg( QFileInfo( selectedManagableFiles.at( 0 ) ).fileName() ),
571659
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
572660
message.setDefaultButton( QMessageBox::Cancel );
573661
message.setButtonText( QMessageBox::Yes, tr( "Delete and Remove Layers" ) );

0 commit comments

Comments
 (0)
Please sign in to comment.