Commit b8231102 authored by Daniel Teske's avatar Daniel Teske

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: default avatarEike Ziller <eike.ziller@theqtcompany.com>
parent bce33353
......@@ -194,6 +194,11 @@ void NavigationSubWidget::setCloseIcon(const QIcon &icon)
m_closeButton->setIcon(icon);
}
QWidget *NavigationSubWidget::widget()
{
return m_navigationWidget;
}
int NavigationSubWidget::factoryIndex() const
{
return m_navigationComboBox->currentIndex();
......
......@@ -72,6 +72,8 @@ public:
Command *command(const QString &title) const;
void setCloseIcon(const QIcon &icon);
QWidget *widget();
signals:
void splitMe(int factoryIndex);
void closeMe();
......
......@@ -270,14 +270,14 @@ void NavigationWidget::activateSubWidget()
activateSubWidget(id);
}
void NavigationWidget::activateSubWidget(Id factoryId)
QWidget *NavigationWidget::activateSubWidget(Id factoryId)
{
setShown(true);
foreach (Internal::NavigationSubWidget *subWidget, d->m_subWidgets) {
if (subWidget->factory()->id() == factoryId) {
subWidget->setFocusWidget();
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()->setFocusWidget();
ICore::raiseWindow(this);
return d->m_subWidgets.first()->widget();
}
}
......
......@@ -87,7 +87,7 @@ public:
void saveSettings(QSettings *settings);
void restoreSettings(QSettings *settings);
void activateSubWidget(Id factoryId);
QWidget *activateSubWidget(Id factoryId);
void closeSubWidgets();
bool isShown() const;
......
......@@ -286,13 +286,13 @@ ProjectOpenerAndCloser::~ProjectOpenerAndCloser()
ProjectInfo ProjectOpenerAndCloser::open(const QString &projectFile, bool configureAsExampleProject)
{
QString error;
Project *project = ProjectExplorerPlugin::openProject(projectFile, &error);
if (!error.isEmpty())
qWarning() << error;
if (!project)
ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProject(projectFile);
if (!result) {
qWarning() << result.errorMessage() << result.alreadyOpen();
return ProjectInfo();
}
Project *project = result.project();
if (configureAsExampleProject)
project->configureAsExampleProject(QStringList());
......
......@@ -3245,15 +3245,15 @@ void DebuggerPluginPrivate::testLoadProject(const QString &proFile, const TestCa
this, &DebuggerPluginPrivate::testProjectLoaded);
m_testCallbacks.append(cb);
QString error;
if (ProjectExplorerPlugin::openProject(proFile, &error)) {
ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProject(proFile);
if (result) {
// Will end up in callback below due to the connections to
// signal currentProjectChanged().
return;
}
// 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);
m_testCallbacks.pop_back();
}
......
......@@ -525,8 +525,12 @@ bool CustomProjectWizard::postGenerateOpen(const Core::GeneratedFiles &l, QStrin
// Post-Generate: Open the project and the editors as desired
foreach (const Core::GeneratedFile &file, l) {
if (file.attributes() & Core::GeneratedFile::OpenProjectAttribute) {
if (!ProjectExplorerPlugin::openProject(file.path(), errorMessage))
ProjectExplorerPlugin::OpenProjectResult result
= ProjectExplorerPlugin::openProject(file.path());
if (!result) {
return false;
*errorMessage = result.errorMessage();
}
}
}
return BaseFileWizardFactory::postGenerateOpenEditors(l, errorMessage);
......
......@@ -309,8 +309,10 @@ void JsonWizard::openFiles(const JsonWizard::GeneratorFiles &files)
break;
}
if (file.attributes() & Core::GeneratedFile::OpenProjectAttribute) {
Project *project = ProjectExplorerPlugin::instance()->openProject(file.path(), &errorMessage);
if (!project) {
ProjectExplorerPlugin::OpenProjectResult result
= ProjectExplorerPlugin::instance()->openProject(file.path());
if (!result) {
errorMessage = result.errorMessage();
if (errorMessage.isEmpty()) {
errorMessage = QCoreApplication::translate("ProjectExplorer::JsonWizard",
"Failed to open \"%1\" as a project.")
......
......@@ -1410,11 +1410,11 @@ void ProjectExplorerPluginPrivate::loadAction()
dd->m_projectFilterString);
if (filename.isEmpty())
return;
QString errorMessage;
ProjectExplorerPlugin::openProject(filename, &errorMessage);
if (!errorMessage.isEmpty())
QMessageBox::critical(ICore::mainWindow(), tr("Failed to open project."), errorMessage);
ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProject(filename);
if (!result)
ProjectExplorerPlugin::showOpenProjectError(result);
updateActions();
}
......@@ -1495,11 +1495,10 @@ void ProjectExplorerPlugin::extensionsInitialized()
auto factory = new IDocumentFactory;
factory->setOpener([this](const QString &fileName) -> IDocument* {
QString errorMessage;
ProjectExplorerPlugin::openProject(fileName, &errorMessage);
if (!errorMessage.isEmpty())
QMessageBox::critical(ICore::mainWindow(),
tr("Failed to open project."), errorMessage);
OpenProjectResult result = ProjectExplorerPlugin::openProject(fileName);
if (!result)
showOpenProjectError(result);
return 0;
});
......@@ -1653,23 +1652,52 @@ void ProjectExplorerPluginPrivate::savePersistentSettings()
void ProjectExplorerPlugin::openProjectWelcomePage(const QString &fileName)
{
QString errorMessage;
openProject(fileName, &errorMessage);
if (!errorMessage.isEmpty())
QMessageBox::critical(ICore::mainWindow(), tr("Failed to Open Project"), errorMessage);
OpenProjectResult result = openProject(fileName);
if (!result)
showOpenProjectError(result);
}
Project *ProjectExplorerPlugin::openProject(const QString &fileName, QString *errorString)
ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProject(const QString &fileName)
{
if (debug)
qDebug() << "ProjectExplorerPlugin::openProject";
QList<Project *> list = openProjects(QStringList() << fileName, errorString);
if (list.isEmpty())
return 0;
dd->addToRecentProjects(fileName, list.first()->displayName());
SessionManager::setStartupProject(list.first());
return list.first();
OpenProjectResult result = openProjects(QStringList() << fileName);
Project *project = result.project();
if (!project)
return result;
dd->addToRecentProjects(fileName, project->displayName());
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()
......@@ -1677,17 +1705,17 @@ static QList<IProjectManager*> allProjectManagers()
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;
if (!errorString->isEmpty())
errorString->append(QLatin1Char('\n'));
errorString->append(error);
if (!errorString.isEmpty())
errorString.append(QLatin1Char('\n'));
errorString.append(error);
}
QList<Project *> ProjectExplorerPlugin::openProjects(const QStringList &fileNames, QString *errorString)
ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(const QStringList &fileNames)
{
if (debug)
qDebug() << "ProjectExplorerPlugin - opening projects " << fileNames;
......@@ -1695,6 +1723,8 @@ QList<Project *> ProjectExplorerPlugin::openProjects(const QStringList &fileName
const QList<IProjectManager*> projectManagers = allProjectManagers();
QList<Project*> openedPro;
QList<Project *> alreadyOpen;
QString errorString;
foreach (const QString &fileName, fileNames) {
QTC_ASSERT(!fileName.isEmpty(), continue);
......@@ -1705,13 +1735,12 @@ QList<Project *> ProjectExplorerPlugin::openProjects(const QStringList &fileName
bool found = false;
foreach (Project *pi, SessionManager::projects()) {
if (filePath == pi->projectFilePath().toString()) {
alreadyOpen.append(pi);
found = true;
break;
}
}
if (found) {
appendError(errorString, tr("Failed opening project \"%1\": Project already open.")
.arg(QDir::toNativeSeparators(fileName)));
SessionManager::reportProjectLoadingProgress();
continue;
}
......@@ -1767,7 +1796,7 @@ QList<Project *> ProjectExplorerPlugin::openProjects(const QStringList &fileName
ModeManager::setFocusToCurrentMode();
}
return openedPro;
return OpenProjectResult(openedPro, alreadyOpen, errorString);
}
void ProjectExplorerPluginPrivate::updateWelcomePage()
......@@ -2900,10 +2929,10 @@ void ProjectExplorerPluginPrivate::openRecentProject(const QString &fileName)
qDebug() << "ProjectExplorerPlugin::openRecentProject()";
if (!fileName.isEmpty()) {
QString errorMessage;
ProjectExplorerPlugin::openProject(fileName, &errorMessage);
if (!errorMessage.isEmpty())
QMessageBox::critical(ICore::mainWindow(), tr("Failed to open project."), errorMessage);
ProjectExplorerPlugin::OpenProjectResult result
= ProjectExplorerPlugin::openProject(fileName);
if (!result)
ProjectExplorerPlugin::showOpenProjectError(result);
}
}
......
......@@ -72,8 +72,48 @@ public:
static ProjectExplorerPlugin *instance();
static Project *openProject(const QString &fileName, QString *error);
static QList<Project *> openProjects(const QStringList &fileNames, QString *error);
class OpenProjectResult
{
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);
static void unloadProject(Project *project);
......
......@@ -277,6 +277,9 @@ const char QML_PROFILER_RUN_MODE[]="RunConfiguration.QmlProfilerRunMode";
const char DEBUG_RUN_MODE[]="RunConfiguration.DebugRunMode";
const char DEBUG_RUN_MODE_WITH_BREAK_ON_MAIN[]="RunConfiguration.DebugRunModeWithBreakOnMain";
// Navigation Widget
const char PROJECTTREE_ID[] = "Projects";
} // namespace Constants
} // namespace ProjectExplorer
......
......@@ -46,6 +46,8 @@
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/navigationwidget.h>
#include <coreplugin/modemanager.h>
#include <QApplication>
#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()
{
m_focusForContextMenu = 0;
......
......@@ -66,6 +66,8 @@ public:
static void showContextMenu(Internal::ProjectTreeWidget *focus, const QPoint &globalPos, Node *node);
static void highlightProject(Project *project, const QString &message);
signals:
void currentProjectChanged(ProjectExplorer::Project *project);
void currentNodeChanged(ProjectExplorer::Node *node, ProjectExplorer::Project *project);
......
......@@ -48,6 +48,7 @@
#include <utils/navigationtreeview.h>
#include <utils/algorithm.h>
#include <utils/tooltip/tooltip.h>
#include <QDebug>
#include <QSettings>
......@@ -480,6 +481,17 @@ void ProjectTreeWidget::sync(Node *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)
{
QModelIndex index = m_view->indexAt(pos);
......@@ -563,7 +575,7 @@ ProjectTreeWidgetFactory::ProjectTreeWidgetFactory()
{
setDisplayName(tr("Projects"));
setPriority(100);
setId("Projects");
setId(ProjectExplorer::Constants::PROJECTTREE_ID);
setActivationSequence(QKeySequence(UseMacShortcuts ? tr("Meta+X") : tr("Alt+X")));
}
......
......@@ -68,6 +68,7 @@ public:
QToolButton *toggleSync();
Node *currentNode();
void sync(ProjectExplorer::Node *node);
void showMessage(ProjectExplorer::Node *node, const QString &message);
static Node *nodeForFile(const Utils::FileName &fileName);
static Node *mostExpandedNode(const QList<Node*> &nodes);
......
......@@ -870,11 +870,10 @@ void SessionManagerPrivate::restoreProjects(const QStringList &fileList)
// Keep projects that failed to load in the session!
m_failedProjects = fileList;
if (!fileList.isEmpty()) {
QString errors;
QList<Project *> projects = ProjectExplorerPlugin::openProjects(fileList, &errors);
if (!errors.isEmpty())
QMessageBox::critical(ICore::mainWindow(), SessionManager::tr("Failed to open project"), errors);
foreach (Project *p, projects)
ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProjects(fileList);
if (!result)
ProjectExplorerPlugin::showOpenProjectError(result);
foreach (Project *p, result.projects())
m_failedProjects.removeAll(p->projectFilePath().toString());
}
}
......
......@@ -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
QString errorMessage;
if (proFile.isEmpty())
return;
if (ProjectExplorer::ProjectExplorerPlugin::instance()->openProject(proFile, &errorMessage)) {
ProjectExplorer::ProjectExplorerPlugin::OpenProjectResult result =
ProjectExplorer::ProjectExplorerPlugin::instance()->openProject(proFile);
if (result) {
Core::ICore::openFiles(filesToOpen);
Core::ModeManager::activateMode(Core::Constants::MODE_EDIT);
if (help.isValid())
openHelpInExtraWindow(help.toString());
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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment