Open Project: Don't show a error dialog for duplicated projects

Instead, switch to edit mode, show sidebar, scroll to project,
and show a tooltip next to the project.

The tooltip is somewhat easy to miss, but this is a clear improvement
in most cases.

Change-Id: Icd27f76e7d434f33e731b6fd56473ff913986a89
Task-number: QTCREATORBUG-8422
Reviewed-by: Eike Ziller <eike.ziller@theqtcompany.com>
This commit is contained in:
Daniel Teske
2015-08-20 14:47:02 +02:00
parent bce3335365
commit b82311024d
17 changed files with 175 additions and 59 deletions

View File

@@ -194,6 +194,11 @@ void NavigationSubWidget::setCloseIcon(const QIcon &icon)
m_closeButton->setIcon(icon); m_closeButton->setIcon(icon);
} }
QWidget *NavigationSubWidget::widget()
{
return m_navigationWidget;
}
int NavigationSubWidget::factoryIndex() const int NavigationSubWidget::factoryIndex() const
{ {
return m_navigationComboBox->currentIndex(); return m_navigationComboBox->currentIndex();

View File

@@ -72,6 +72,8 @@ public:
Command *command(const QString &title) const; Command *command(const QString &title) const;
void setCloseIcon(const QIcon &icon); void setCloseIcon(const QIcon &icon);
QWidget *widget();
signals: signals:
void splitMe(int factoryIndex); void splitMe(int factoryIndex);
void closeMe(); void closeMe();

View File

@@ -270,14 +270,14 @@ void NavigationWidget::activateSubWidget()
activateSubWidget(id); activateSubWidget(id);
} }
void NavigationWidget::activateSubWidget(Id factoryId) QWidget *NavigationWidget::activateSubWidget(Id factoryId)
{ {
setShown(true); setShown(true);
foreach (Internal::NavigationSubWidget *subWidget, d->m_subWidgets) { foreach (Internal::NavigationSubWidget *subWidget, d->m_subWidgets) {
if (subWidget->factory()->id() == factoryId) { if (subWidget->factory()->id() == factoryId) {
subWidget->setFocusWidget(); subWidget->setFocusWidget();
ICore::raiseWindow(this); ICore::raiseWindow(this);
return; return subWidget->widget();
} }
} }
@@ -286,6 +286,7 @@ void NavigationWidget::activateSubWidget(Id factoryId)
d->m_subWidgets.first()->setFactoryIndex(index); d->m_subWidgets.first()->setFactoryIndex(index);
d->m_subWidgets.first()->setFocusWidget(); d->m_subWidgets.first()->setFocusWidget();
ICore::raiseWindow(this); ICore::raiseWindow(this);
return d->m_subWidgets.first()->widget();
} }
} }

View File

@@ -87,7 +87,7 @@ public:
void saveSettings(QSettings *settings); void saveSettings(QSettings *settings);
void restoreSettings(QSettings *settings); void restoreSettings(QSettings *settings);
void activateSubWidget(Id factoryId); QWidget *activateSubWidget(Id factoryId);
void closeSubWidgets(); void closeSubWidgets();
bool isShown() const; bool isShown() const;

View File

@@ -286,13 +286,13 @@ ProjectOpenerAndCloser::~ProjectOpenerAndCloser()
ProjectInfo ProjectOpenerAndCloser::open(const QString &projectFile, bool configureAsExampleProject) ProjectInfo ProjectOpenerAndCloser::open(const QString &projectFile, bool configureAsExampleProject)
{ {
QString error; ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProject(projectFile);
Project *project = ProjectExplorerPlugin::openProject(projectFile, &error); if (!result) {
if (!error.isEmpty()) qWarning() << result.errorMessage() << result.alreadyOpen();
qWarning() << error;
if (!project)
return ProjectInfo(); return ProjectInfo();
}
Project *project = result.project();
if (configureAsExampleProject) if (configureAsExampleProject)
project->configureAsExampleProject(QStringList()); project->configureAsExampleProject(QStringList());

View File

@@ -3245,15 +3245,15 @@ void DebuggerPluginPrivate::testLoadProject(const QString &proFile, const TestCa
this, &DebuggerPluginPrivate::testProjectLoaded); this, &DebuggerPluginPrivate::testProjectLoaded);
m_testCallbacks.append(cb); m_testCallbacks.append(cb);
QString error; ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProject(proFile);
if (ProjectExplorerPlugin::openProject(proFile, &error)) { if (result) {
// Will end up in callback below due to the connections to // Will end up in callback below due to the connections to
// signal currentProjectChanged(). // signal currentProjectChanged().
return; return;
} }
// Project opening failed. Eat the unused callback. // Project opening failed. Eat the unused callback.
qWarning("Cannot open %s: %s", qPrintable(proFile), qPrintable(error)); qWarning("Cannot open %s: %s", qPrintable(proFile), qPrintable(result.errorMessage()));
QVERIFY(false); QVERIFY(false);
m_testCallbacks.pop_back(); m_testCallbacks.pop_back();
} }

View File

@@ -525,8 +525,12 @@ bool CustomProjectWizard::postGenerateOpen(const Core::GeneratedFiles &l, QStrin
// Post-Generate: Open the project and the editors as desired // Post-Generate: Open the project and the editors as desired
foreach (const Core::GeneratedFile &file, l) { foreach (const Core::GeneratedFile &file, l) {
if (file.attributes() & Core::GeneratedFile::OpenProjectAttribute) { if (file.attributes() & Core::GeneratedFile::OpenProjectAttribute) {
if (!ProjectExplorerPlugin::openProject(file.path(), errorMessage)) ProjectExplorerPlugin::OpenProjectResult result
= ProjectExplorerPlugin::openProject(file.path());
if (!result) {
return false; return false;
*errorMessage = result.errorMessage();
}
} }
} }
return BaseFileWizardFactory::postGenerateOpenEditors(l, errorMessage); return BaseFileWizardFactory::postGenerateOpenEditors(l, errorMessage);

View File

@@ -309,8 +309,10 @@ void JsonWizard::openFiles(const JsonWizard::GeneratorFiles &files)
break; break;
} }
if (file.attributes() & Core::GeneratedFile::OpenProjectAttribute) { if (file.attributes() & Core::GeneratedFile::OpenProjectAttribute) {
Project *project = ProjectExplorerPlugin::instance()->openProject(file.path(), &errorMessage); ProjectExplorerPlugin::OpenProjectResult result
if (!project) { = ProjectExplorerPlugin::instance()->openProject(file.path());
if (!result) {
errorMessage = result.errorMessage();
if (errorMessage.isEmpty()) { if (errorMessage.isEmpty()) {
errorMessage = QCoreApplication::translate("ProjectExplorer::JsonWizard", errorMessage = QCoreApplication::translate("ProjectExplorer::JsonWizard",
"Failed to open \"%1\" as a project.") "Failed to open \"%1\" as a project.")

View File

@@ -1410,11 +1410,11 @@ void ProjectExplorerPluginPrivate::loadAction()
dd->m_projectFilterString); dd->m_projectFilterString);
if (filename.isEmpty()) if (filename.isEmpty())
return; return;
QString errorMessage;
ProjectExplorerPlugin::openProject(filename, &errorMessage);
if (!errorMessage.isEmpty()) ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProject(filename);
QMessageBox::critical(ICore::mainWindow(), tr("Failed to open project."), errorMessage); if (!result)
ProjectExplorerPlugin::showOpenProjectError(result);
updateActions(); updateActions();
} }
@@ -1495,11 +1495,10 @@ void ProjectExplorerPlugin::extensionsInitialized()
auto factory = new IDocumentFactory; auto factory = new IDocumentFactory;
factory->setOpener([this](const QString &fileName) -> IDocument* { factory->setOpener([this](const QString &fileName) -> IDocument* {
QString errorMessage;
ProjectExplorerPlugin::openProject(fileName, &errorMessage); OpenProjectResult result = ProjectExplorerPlugin::openProject(fileName);
if (!errorMessage.isEmpty()) if (!result)
QMessageBox::critical(ICore::mainWindow(), showOpenProjectError(result);
tr("Failed to open project."), errorMessage);
return 0; return 0;
}); });
@@ -1653,23 +1652,52 @@ void ProjectExplorerPluginPrivate::savePersistentSettings()
void ProjectExplorerPlugin::openProjectWelcomePage(const QString &fileName) void ProjectExplorerPlugin::openProjectWelcomePage(const QString &fileName)
{ {
QString errorMessage; OpenProjectResult result = openProject(fileName);
openProject(fileName, &errorMessage); if (!result)
if (!errorMessage.isEmpty()) showOpenProjectError(result);
QMessageBox::critical(ICore::mainWindow(), tr("Failed to Open Project"), errorMessage);
} }
Project *ProjectExplorerPlugin::openProject(const QString &fileName, QString *errorString) ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProject(const QString &fileName)
{ {
if (debug) if (debug)
qDebug() << "ProjectExplorerPlugin::openProject"; qDebug() << "ProjectExplorerPlugin::openProject";
QList<Project *> list = openProjects(QStringList() << fileName, errorString); OpenProjectResult result = openProjects(QStringList() << fileName);
if (list.isEmpty()) Project *project = result.project();
return 0; if (!project)
dd->addToRecentProjects(fileName, list.first()->displayName()); return result;
SessionManager::setStartupProject(list.first()); dd->addToRecentProjects(fileName, project->displayName());
return list.first(); SessionManager::setStartupProject(project);
return result;
}
void ProjectExplorerPlugin::showOpenProjectError(const OpenProjectResult &result)
{
if (result)
return;
// Potentially both errorMessage and alreadyOpen could contain information
// that should be shown to the user.
// BUT, if Creator opens only a single project, this can lead
// to either
// - No error
// - A errorMessage
// - A single project in alreadyOpen
// The only place where multiple projects are opened is in session restore
// where the already open case should never happen, thus
// the following code uses those assumptions to make the code simpler
QString errorMessage = result.errorMessage();
if (!errorMessage.isEmpty()) {
// ignore alreadyOpen
QMessageBox::critical(ICore::mainWindow(), tr("Failed to Open Project"), errorMessage);
} else {
// ignore multiple alreadyOpen
Project *alreadyOpen = result.alreadyOpen().first();
ProjectTree::highlightProject(alreadyOpen,
tr("<h3>Project already open</h3>"));
}
} }
static QList<IProjectManager*> allProjectManagers() static QList<IProjectManager*> allProjectManagers()
@@ -1677,17 +1705,17 @@ static QList<IProjectManager*> allProjectManagers()
return ExtensionSystem::PluginManager::getObjects<IProjectManager>(); return ExtensionSystem::PluginManager::getObjects<IProjectManager>();
} }
static void appendError(QString *errorString, const QString &error) static void appendError(QString &errorString, const QString &error)
{ {
if (!errorString || error.isEmpty()) if (error.isEmpty())
return; return;
if (!errorString->isEmpty()) if (!errorString.isEmpty())
errorString->append(QLatin1Char('\n')); errorString.append(QLatin1Char('\n'));
errorString->append(error); errorString.append(error);
} }
QList<Project *> ProjectExplorerPlugin::openProjects(const QStringList &fileNames, QString *errorString) ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(const QStringList &fileNames)
{ {
if (debug) if (debug)
qDebug() << "ProjectExplorerPlugin - opening projects " << fileNames; qDebug() << "ProjectExplorerPlugin - opening projects " << fileNames;
@@ -1695,6 +1723,8 @@ QList<Project *> ProjectExplorerPlugin::openProjects(const QStringList &fileName
const QList<IProjectManager*> projectManagers = allProjectManagers(); const QList<IProjectManager*> projectManagers = allProjectManagers();
QList<Project*> openedPro; QList<Project*> openedPro;
QList<Project *> alreadyOpen;
QString errorString;
foreach (const QString &fileName, fileNames) { foreach (const QString &fileName, fileNames) {
QTC_ASSERT(!fileName.isEmpty(), continue); QTC_ASSERT(!fileName.isEmpty(), continue);
@@ -1705,13 +1735,12 @@ QList<Project *> ProjectExplorerPlugin::openProjects(const QStringList &fileName
bool found = false; bool found = false;
foreach (Project *pi, SessionManager::projects()) { foreach (Project *pi, SessionManager::projects()) {
if (filePath == pi->projectFilePath().toString()) { if (filePath == pi->projectFilePath().toString()) {
alreadyOpen.append(pi);
found = true; found = true;
break; break;
} }
} }
if (found) { if (found) {
appendError(errorString, tr("Failed opening project \"%1\": Project already open.")
.arg(QDir::toNativeSeparators(fileName)));
SessionManager::reportProjectLoadingProgress(); SessionManager::reportProjectLoadingProgress();
continue; continue;
} }
@@ -1767,7 +1796,7 @@ QList<Project *> ProjectExplorerPlugin::openProjects(const QStringList &fileName
ModeManager::setFocusToCurrentMode(); ModeManager::setFocusToCurrentMode();
} }
return openedPro; return OpenProjectResult(openedPro, alreadyOpen, errorString);
} }
void ProjectExplorerPluginPrivate::updateWelcomePage() void ProjectExplorerPluginPrivate::updateWelcomePage()
@@ -2900,10 +2929,10 @@ void ProjectExplorerPluginPrivate::openRecentProject(const QString &fileName)
qDebug() << "ProjectExplorerPlugin::openRecentProject()"; qDebug() << "ProjectExplorerPlugin::openRecentProject()";
if (!fileName.isEmpty()) { if (!fileName.isEmpty()) {
QString errorMessage; ProjectExplorerPlugin::OpenProjectResult result
ProjectExplorerPlugin::openProject(fileName, &errorMessage); = ProjectExplorerPlugin::openProject(fileName);
if (!errorMessage.isEmpty()) if (!result)
QMessageBox::critical(ICore::mainWindow(), tr("Failed to open project."), errorMessage); ProjectExplorerPlugin::showOpenProjectError(result);
} }
} }

View File

@@ -72,8 +72,48 @@ public:
static ProjectExplorerPlugin *instance(); static ProjectExplorerPlugin *instance();
static Project *openProject(const QString &fileName, QString *error); class OpenProjectResult
static QList<Project *> openProjects(const QStringList &fileNames, QString *error); {
public:
OpenProjectResult(QList<Project *> projects, QList<Project *> alreadyOpen,
const QString &errorMessage)
: m_projects(projects), m_alreadyOpen(alreadyOpen),
m_errorMessage(errorMessage)
{}
explicit operator bool() const
{
return m_errorMessage.isEmpty() && m_alreadyOpen.isEmpty();
}
Project *project() const
{
return m_projects.isEmpty() ? 0 : m_projects.first();
}
QList<Project *> projects() const
{
return m_projects;
}
QString errorMessage() const
{
return m_errorMessage;
}
QList<Project *> alreadyOpen() const
{
return m_alreadyOpen;
}
private:
QList<Project *> m_projects;
QList<Project *> m_alreadyOpen;
QString m_errorMessage;
};
static OpenProjectResult openProject(const QString &fileName);
static OpenProjectResult openProjects(const QStringList &fileNames);
static void showOpenProjectError(const OpenProjectResult &result);
Q_SLOT void openProjectWelcomePage(const QString &fileName); Q_SLOT void openProjectWelcomePage(const QString &fileName);
static void unloadProject(Project *project); static void unloadProject(Project *project);

View File

@@ -277,6 +277,9 @@ const char QML_PROFILER_RUN_MODE[]="RunConfiguration.QmlProfilerRunMode";
const char DEBUG_RUN_MODE[]="RunConfiguration.DebugRunMode"; const char DEBUG_RUN_MODE[]="RunConfiguration.DebugRunMode";
const char DEBUG_RUN_MODE_WITH_BREAK_ON_MAIN[]="RunConfiguration.DebugRunModeWithBreakOnMain"; const char DEBUG_RUN_MODE_WITH_BREAK_ON_MAIN[]="RunConfiguration.DebugRunModeWithBreakOnMain";
// Navigation Widget
const char PROJECTTREE_ID[] = "Projects";
} // namespace Constants } // namespace Constants
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -46,6 +46,8 @@
#include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/documentmanager.h> #include <coreplugin/documentmanager.h>
#include <coreplugin/navigationwidget.h>
#include <coreplugin/modemanager.h>
#include <QApplication> #include <QApplication>
#include <QMenu> #include <QMenu>
@@ -531,6 +533,19 @@ void ProjectTree::showContextMenu(ProjectTreeWidget *focus, const QPoint &global
} }
} }
void ProjectTree::highlightProject(Project *project, const QString &message)
{
Core::ModeManager::activateMode(Core::Constants::MODE_EDIT);
Core::NavigationWidget *navigation = Core::NavigationWidget::instance();
// Shows and focusses a project tree
QWidget *widget = navigation->activateSubWidget(ProjectExplorer::Constants::PROJECTTREE_ID);
if (auto *projectTreeWidget = qobject_cast<ProjectTreeWidget *>(widget))
projectTreeWidget->showMessage(project->rootProjectNode(), message);
}
void ProjectTree::hideContextMenu() void ProjectTree::hideContextMenu()
{ {
m_focusForContextMenu = 0; m_focusForContextMenu = 0;

View File

@@ -66,6 +66,8 @@ public:
static void showContextMenu(Internal::ProjectTreeWidget *focus, const QPoint &globalPos, Node *node); static void showContextMenu(Internal::ProjectTreeWidget *focus, const QPoint &globalPos, Node *node);
static void highlightProject(Project *project, const QString &message);
signals: signals:
void currentProjectChanged(ProjectExplorer::Project *project); void currentProjectChanged(ProjectExplorer::Project *project);
void currentNodeChanged(ProjectExplorer::Node *node, ProjectExplorer::Project *project); void currentNodeChanged(ProjectExplorer::Node *node, ProjectExplorer::Project *project);

View File

@@ -48,6 +48,7 @@
#include <utils/navigationtreeview.h> #include <utils/navigationtreeview.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/tooltip/tooltip.h>
#include <QDebug> #include <QDebug>
#include <QSettings> #include <QSettings>
@@ -480,6 +481,17 @@ void ProjectTreeWidget::sync(Node *node)
setCurrentItem(node); setCurrentItem(node);
} }
void ProjectTreeWidget::showMessage(Node *node, const QString &message)
{
QModelIndex idx = m_model->indexForNode(node);
m_view->setCurrentIndex(idx);
m_view->scrollTo(idx);
QPoint pos = m_view->mapToGlobal(m_view->visualRect(idx).bottomLeft());
pos -= Utils::ToolTip::offsetFromPosition();
Utils::ToolTip::show(pos, message);
}
void ProjectTreeWidget::showContextMenu(const QPoint &pos) void ProjectTreeWidget::showContextMenu(const QPoint &pos)
{ {
QModelIndex index = m_view->indexAt(pos); QModelIndex index = m_view->indexAt(pos);
@@ -563,7 +575,7 @@ ProjectTreeWidgetFactory::ProjectTreeWidgetFactory()
{ {
setDisplayName(tr("Projects")); setDisplayName(tr("Projects"));
setPriority(100); setPriority(100);
setId("Projects"); setId(ProjectExplorer::Constants::PROJECTTREE_ID);
setActivationSequence(QKeySequence(UseMacShortcuts ? tr("Meta+X") : tr("Alt+X"))); setActivationSequence(QKeySequence(UseMacShortcuts ? tr("Meta+X") : tr("Alt+X")));
} }

View File

@@ -68,6 +68,7 @@ public:
QToolButton *toggleSync(); QToolButton *toggleSync();
Node *currentNode(); Node *currentNode();
void sync(ProjectExplorer::Node *node); void sync(ProjectExplorer::Node *node);
void showMessage(ProjectExplorer::Node *node, const QString &message);
static Node *nodeForFile(const Utils::FileName &fileName); static Node *nodeForFile(const Utils::FileName &fileName);
static Node *mostExpandedNode(const QList<Node*> &nodes); static Node *mostExpandedNode(const QList<Node*> &nodes);

View File

@@ -870,11 +870,10 @@ void SessionManagerPrivate::restoreProjects(const QStringList &fileList)
// Keep projects that failed to load in the session! // Keep projects that failed to load in the session!
m_failedProjects = fileList; m_failedProjects = fileList;
if (!fileList.isEmpty()) { if (!fileList.isEmpty()) {
QString errors; ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProjects(fileList);
QList<Project *> projects = ProjectExplorerPlugin::openProjects(fileList, &errors); if (!result)
if (!errors.isEmpty()) ProjectExplorerPlugin::showOpenProjectError(result);
QMessageBox::critical(ICore::mainWindow(), SessionManager::tr("Failed to open project"), errors); foreach (Project *p, result.projects())
foreach (Project *p, projects)
m_failedProjects.removeAll(p->projectFilePath().toString()); m_failedProjects.removeAll(p->projectFilePath().toString());
} }
} }

View File

@@ -412,18 +412,19 @@ void ExamplesWelcomePage::openProject(const QString &projectFile,
} }
// don't try to load help and files if loading the help request is being cancelled // don't try to load help and files if loading the help request is being cancelled
QString errorMessage;
if (proFile.isEmpty()) if (proFile.isEmpty())
return; return;
if (ProjectExplorer::ProjectExplorerPlugin::instance()->openProject(proFile, &errorMessage)) { ProjectExplorer::ProjectExplorerPlugin::OpenProjectResult result =
ProjectExplorer::ProjectExplorerPlugin::instance()->openProject(proFile);
if (result) {
Core::ICore::openFiles(filesToOpen); Core::ICore::openFiles(filesToOpen);
Core::ModeManager::activateMode(Core::Constants::MODE_EDIT); Core::ModeManager::activateMode(Core::Constants::MODE_EDIT);
if (help.isValid()) if (help.isValid())
openHelpInExtraWindow(help.toString()); openHelpInExtraWindow(help.toString());
Core::ModeManager::activateMode(ProjectExplorer::Constants::MODE_SESSION); Core::ModeManager::activateMode(ProjectExplorer::Constants::MODE_SESSION);
} else {
ProjectExplorer::ProjectExplorerPlugin::showOpenProjectError(result);
} }
if (!errorMessage.isEmpty())
QMessageBox::critical(Core::ICore::mainWindow(), tr("Failed to Open Project"), errorMessage);
} }
ExamplesListModel *ExamplesWelcomePage::examplesModel() const ExamplesListModel *ExamplesWelcomePage::examplesModel() const