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,32 @@ 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 + if (urlInfo(m_url).isDir) { + m_projectDirUrl = m_url.adjusted(QUrl::StripTrailingSlash); + } else { + 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,28 @@ } 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; + bool shouldAsk = false; 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"); + 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 ); @@ -499,8 +511,22 @@ if (writeProjectConfigToFile) { Path projectConfigDir(projectFileUrl); projectConfigDir.setLastPathSegment(QStringLiteral(".kdev4")); - auto delJob = KIO::del(projectConfigDir.toUrl()); - delJob->exec(); + // apparently confusion can arise when an existing project is overridden and the .kdev4 + // directory remains (http://phabricator.kde.org/T6262). But the directory can + // contain settings files for other projects which shouldn't be deleted without + // asking the user. Post another dialog to ask permission, if needed. + if (shouldAsk && QFileInfo(projectConfigDir.toLocalFile()).exists()) { + const auto dotKD4Dir = projectConfigDir.toUrl().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); + int ret = KMessageBox::questionYesNoCancel(qApp->activeWindow(), + i18n("There already exists a .kdev4 directory at %1 .\n" + "Do you have other projects defined there that need to be preserved?", + dotKD4Dir.toLocalFile()), + i18n("Preserve the existing .kdev4 directory?")); + if ( ret == KMessageBox::No ) { + auto delJob = KIO::del(projectConfigDir.toUrl()); + delJob->exec(); + } + } if (!writeProjectSettingsToConfigFile(projectFileUrl, dlg)) { KMessageBox::error(d->m_core->uiControllerInternal()->defaultMainWindow(),