Index: kdevplatform/shell/openprojectdialog.h =================================================================== --- kdevplatform/shell/openprojectdialog.h +++ kdevplatform/shell/openprojectdialog.h @@ -67,6 +67,7 @@ private: bool execNativeDialog(); void validateProjectInfo(); + QUrl m_projectDirUrl; QUrl m_url; QUrl m_selected; QString m_projectName; Index: kdevplatform/shell/openprojectdialog.cpp =================================================================== --- kdevplatform/shell/openprojectdialog.cpp +++ kdevplatform/shell/openprojectdialog.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -73,6 +74,7 @@ const QUrl& repoUrl, IPlugin* vcsOrProviderPlugin, QWidget* parent) : KAssistantDialog( parent ) + , m_projectDirUrl(QUrl()) , m_urlIsDirectory(false) , sourcePage(nullptr) , openPage(nullptr) @@ -272,7 +274,9 @@ } page->populateProjectFileCombo(choices); } - m_url.setPath( m_url.path() + QLatin1Char('/') + m_url.fileName() + QLatin1Char('.') + ShellExtension::getInstance()->projectFileExtension() ); + if (!m_url.toLocalFile().endsWith(QLatin1Char('.') + ShellExtension::getInstance()->projectFileExtension())) { + m_url.setPath( m_url.path() + QLatin1Char('/') + m_url.fileName() + QLatin1Char('.') + ShellExtension::getInstance()->projectFileExtension() ); + } } else { setAppropriate( projectInfoPage, false ); m_url = url; @@ -310,7 +314,35 @@ void OpenProjectDialog::validateProjectName( const QString& name ) { - m_projectName = name; + if (name != m_projectName) { + bool settingName = currentPage() == projectInfoPage; + m_projectName = name; + if (m_projectDirUrl.isEmpty() && settingName) { + // cache the selected project directory + const auto urlInfo = ::urlInfo(m_url); + if (urlInfo.isValid && urlInfo.isDir) { + m_projectDirUrl = m_url.adjusted(QUrl::StripTrailingSlash); + } else { + // if !urlInfo.isValid the url almost certainly refers to a file that doesn't exist (yet) + // With the Generic Makefile proj.manager it can be the project file url, for instance. + m_projectDirUrl = m_url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); + } + } + const QUrl url(m_projectDirUrl); + // construct a version of the project name that's safe for use as a filename: + // TODO: do an additional replace of QDir::separator() with "@"? + QString safeName = m_projectName; + safeName.replace(QRegularExpression(QStringLiteral("[\\\\/]")), QStringLiteral("@")); + safeName = safeName.replace(QLatin1Char(':'), QLatin1Char('=')); + safeName = safeName.replace(QRegExp(QStringLiteral("\\s")), QStringLiteral("_")); + safeName += QLatin1Char('.') + ShellExtension::getInstance()->projectFileExtension(); + // don't interfere with m_url when validateOpenUrl() is also likely to change it + if (settingName) { + m_url.setPath(url.path() + QLatin1Char('/') + safeName); + m_urlIsDirectory = false; + qCDebug(SHELL) << "project name:" << m_projectName << "file name:" << safeName << "in" << url.path(); + } + } validateProjectInfo(); } Index: kdevplatform/shell/projectcontroller.cpp =================================================================== --- kdevplatform/shell/projectcontroller.cpp +++ kdevplatform/shell/projectcontroller.cpp @@ -423,6 +423,9 @@ KSharedConfigPtr cfg = KSharedConfig::openConfig( configPath, KConfig::SimpleConfig ); KConfigGroup grp = cfg->group( "Project" ); QString defaultName = dlg->projectFileUrl().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).fileName(); + qCDebug(SHELL) << "configPath=" << configPath << "defaultName=" << defaultName + << "projName=" << dlg->projectName() << "projectMan=" << dlg->projectManager() + << "grp.Name=" << grp.readEntry( "Name", QString() ) << "grp.Manager=" << grp.readEntry( "Manager", QString() ); return (grp.readEntry( "Name", QString() ) == dlg->projectName() || dlg->projectName() == defaultName) && grp.readEntry( "Manager", QString() ) == dlg->projectManager(); } @@ -438,19 +441,27 @@ } QUrl projectFileUrl = dlg->projectFileUrl(); - qCDebug(SHELL) << "selected project:" << projectFileUrl << dlg->projectName() << dlg->projectManager(); + qCDebug(SHELL) << "selected project:" << projectFileUrl << "selectedUrl=" << dlg->selectedUrl() + << "projectName=" << dlg->projectName() << "projectManager=" << dlg->projectManager(); if ( dlg->projectManager() == QLatin1String("") ) { return projectFileUrl; } // controls if existing project file should be saved bool writeProjectConfigToFile = true; if( projectFileExists( projectFileUrl ) ) { - // check whether config is equal - bool shouldAsk = true; - if( projectFileUrl == dlg->selectedUrl() ) + // check whether we should question the user about overriding an existing project file or not. + // We don't need to do that when the file we're importing (dlg->selectedUrl) is already an + // existing .kdev4 project file (we just verified that it exists): + bool isKDevProject = QFileInfo(dlg->selectedUrl().url()).suffix() == QStringLiteral("kdev4"); + bool shouldAsk = !isKDevProject; + if( !isKDevProject && projectFileUrl == dlg->selectedUrl() ) { + // We're importing a project from another type of project file, post the + // override dialog if there's a discrepancy between the project file URL + // and the information stored in the dialog and the project settings. + qCWarning(SHELL) << "Importing a foreign project type:" << projectFileUrl.url(); if( projectFileUrl.isLocalFile() ) { shouldAsk = !equalProjectFile( projectFileUrl.toLocalFile(), dlg ); @@ -490,18 +501,24 @@ } else if ( ret == KMessageBox::Cancel ) { return QUrl(); - } // else fall through and write new file + } else { + // confusion can arise when an existing project is overridden and the old settings + // file remains (http://phabricator.kde.org/T6262). The .kdev4 directory can + // contain settings files for other projects which shouldn't be deleted, so + // we delete just the settings file for this project. + Path settingsFile(projectFileUrl); + settingsFile.setLastPathSegment(QStringLiteral(".kdev4")); + settingsFile.addPath(projectFileUrl.fileName()); + qCDebug(SHELL) << "Deleting old settings file before overriding it:" << settingsFile; + auto delJob = KIO::del(settingsFile.toUrl()); + delJob->exec(); + } } else { writeProjectConfigToFile = false; } } if (writeProjectConfigToFile) { - Path projectConfigDir(projectFileUrl); - projectConfigDir.setLastPathSegment(QStringLiteral(".kdev4")); - auto delJob = KIO::del(projectConfigDir.toUrl()); - delJob->exec(); - if (!writeProjectSettingsToConfigFile(projectFileUrl, dlg)) { KMessageBox::error(d->m_core->uiControllerInternal()->defaultMainWindow(), i18n("Unable to create configuration file %1", projectFileUrl.url()));