session.cpp 31.5 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29 30 31

#include "session.h"

con's avatar
con committed
32 33 34 35
#include "project.h"
#include "projectexplorer.h"
#include "nodesvisitor.h"
#include "editorconfiguration.h"
36
#include "projectnodes.h"
con's avatar
con committed
37 38 39 40

#include <coreplugin/icore.h>
#include <coreplugin/imode.h>
#include <coreplugin/editormanager/editormanager.h>
41
#include <coreplugin/coreconstants.h>
42
#include <coreplugin/progressmanager/progressmanager.h>
con's avatar
con committed
43 44 45 46
#include <coreplugin/modemanager.h>

#include <texteditor/itexteditor.h>

47
#include <utils/stylehelper.h>
hjk's avatar
hjk committed
48

49 50 51
#include <QDebug>
#include <QDir>
#include <QFileInfo>
hjk's avatar
hjk committed
52

53 54
#include <QMessageBox>
#include <QPushButton>
con's avatar
con committed
55

hjk's avatar
hjk committed
56
namespace { bool debug = false; }
con's avatar
con committed
57

58
using namespace Core;
hjk's avatar
hjk committed
59
using namespace Utils;
60
using namespace ProjectExplorer::Internal;
con's avatar
con committed
61

hjk's avatar
hjk committed
62 63
namespace ProjectExplorer {

64 65 66
/*!
     \class ProjectExplorer::SessionManager

67
     \brief The SessionManager class manages sessions.
68 69

     TODO the interface of this class is not really great.
70 71
     The implementation suffers from that all the functions from the
     public interface just wrap around functions which do the actual work.
72 73
     This could be improved.
*/
con's avatar
con committed
74

hjk's avatar
hjk committed
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
class SessionManagerPrivate
{
public:
    SessionManagerPrivate() :
        m_sessionName(QLatin1String("default")),
        m_virginSession(true),
        m_loadingSession(false),
        m_startupProject(0),
        m_writer(0)
    {}

    bool projectContainsFile(Project *p, const QString &fileName) const;
    void restoreValues(const PersistentSettingsReader &reader);
    void restoreDependencies(const PersistentSettingsReader &reader);
    void restoreStartupProject(const PersistentSettingsReader &reader);
    void restoreEditors(const PersistentSettingsReader &reader);
    void restoreProjects(const QStringList &fileList);
    void askUserAboutFailedProjects();
    void sessionLoadingProgress();

    bool recursiveDependencyCheck(const QString &newDep, const QString &checkDep) const;
    QStringList dependencies(const QString &proName) const;
    QStringList dependenciesOrder() const;
    void dependencies(const QString &proName, QStringList &result) const;

public:
    SessionNode *m_sessionNode;
    QString m_sessionName;
    bool m_virginSession;

    mutable QStringList m_sessions;

    mutable QHash<Project *, QStringList> m_projectFileCache;
    bool m_loadingSession;

    Project *m_startupProject;
    QList<Project *> m_projects;
    QStringList m_failedProjects;
    QMap<QString, QStringList> m_depMap;
    QMap<QString, QVariant> m_values;
    QFutureInterface<void> m_future;
    PersistentSettingsWriter *m_writer;
};

static SessionManager *m_instance = 0;
static SessionManagerPrivate *d = 0;

hjk's avatar
hjk committed
122
SessionManager::SessionManager(QObject *parent)
hjk's avatar
hjk committed
123
  : QObject(parent)
con's avatar
con committed
124
{
hjk's avatar
hjk committed
125 126 127 128 129
    m_instance = this;
    d = new SessionManagerPrivate;

    d->m_sessionNode = new SessionNode(this);

130
    connect(ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode*)),
con's avatar
con committed
131
            this, SLOT(saveActiveMode(Core::IMode*)));
132

133
    connect(EditorManager::instance(), SIGNAL(editorCreated(Core::IEditor*,QString)),
Robert Loehning's avatar
Robert Loehning committed
134 135
            this, SLOT(configureEditor(Core::IEditor*,QString)));
    connect(ProjectExplorerPlugin::instance(), SIGNAL(currentProjectChanged(ProjectExplorer::Project*)),
con's avatar
con committed
136
            this, SLOT(updateWindowTitle()));
137
    connect(EditorManager::instance(), SIGNAL(editorOpened(Core::IEditor*)),
138
            this, SLOT(markSessionFileDirty()));
139
    connect(EditorManager::instance(), SIGNAL(editorsClosed(QList<Core::IEditor*>)),
140
            this, SLOT(markSessionFileDirty()));
hjk's avatar
hjk committed
141
}
con's avatar
con committed
142 143 144

SessionManager::~SessionManager()
{
hjk's avatar
hjk committed
145 146 147
    emit m_instance->aboutToUnloadSession(d->m_sessionName);
    delete d->m_writer;
    delete d;
con's avatar
con committed
148 149
}

hjk's avatar
hjk committed
150 151 152 153
QObject *SessionManager::instance()
{
   return m_instance;
}
con's avatar
con committed
154

hjk's avatar
hjk committed
155
bool SessionManager::isDefaultVirgin()
con's avatar
con committed
156
{
hjk's avatar
hjk committed
157
    return isDefaultSession(d->m_sessionName) && d->m_virginSession;
con's avatar
con committed
158 159
}

hjk's avatar
hjk committed
160
bool SessionManager::isDefaultSession(const QString &session)
con's avatar
con committed
161
{
hjk's avatar
hjk committed
162
    return session == QLatin1String("default");
con's avatar
con committed
163 164 165 166 167
}


void SessionManager::saveActiveMode(Core::IMode *mode)
{
168
    setValue(QLatin1String("ActiveMode"), mode->id().toString());
con's avatar
con committed
169 170 171 172 173 174
}

void SessionManager::clearProjectFileCache()
{
    // If triggered by the fileListChanged signal of one project
    // only invalidate cache for this project
hjk's avatar
hjk committed
175
    Project *pro = qobject_cast<Project*>(m_instance->sender());
con's avatar
con committed
176
    if (pro)
hjk's avatar
hjk committed
177
        d->m_projectFileCache.remove(pro);
con's avatar
con committed
178
    else
hjk's avatar
hjk committed
179
        d->m_projectFileCache.clear();
con's avatar
con committed
180 181
}

hjk's avatar
hjk committed
182
bool SessionManagerPrivate::recursiveDependencyCheck(const QString &newDep, const QString &checkDep) const
con's avatar
con committed
183 184 185 186
{
    if (newDep == checkDep)
        return false;

hjk's avatar
hjk committed
187
    foreach (const QString &dependency, m_depMap.value(checkDep))
con's avatar
con committed
188 189 190 191 192 193
        if (!recursiveDependencyCheck(newDep, dependency))
            return false;

    return true;
}

194
/*
195
 * The dependency management exposes an interface based on projects, but
196 197 198 199 200
 * is internally purely string based. This is suboptimal. Probably it would be
 * nicer to map the filenames to projects on load and only map it back to
 * filenames when saving.
 */

hjk's avatar
hjk committed
201
QList<Project *> SessionManager::dependencies(const Project *project)
202
{
203
    const QString proName = project->projectFilePath().toString();
hjk's avatar
hjk committed
204
    const QStringList proDeps = d->m_depMap.value(proName);
205 206 207 208 209 210 211 212 213 214

    QList<Project *> projects;
    foreach (const QString &dep, proDeps) {
        if (Project *pro = projectForFile(dep))
            projects += pro;
    }

    return projects;
}

hjk's avatar
hjk committed
215
bool SessionManager::hasDependency(const Project *project, const Project *depProject)
con's avatar
con committed
216
{
217 218
    const QString proName = project->projectFilePath().toString();
    const QString depName = depProject->projectFilePath().toString();
con's avatar
con committed
219

hjk's avatar
hjk committed
220
    const QStringList proDeps = d->m_depMap.value(proName);
con's avatar
con committed
221 222 223
    return proDeps.contains(depName);
}

hjk's avatar
hjk committed
224
bool SessionManager::canAddDependency(const Project *project, const Project *depProject)
con's avatar
con committed
225
{
226 227
    const QString newDep = project->projectFilePath().toString();
    const QString checkDep = depProject->projectFilePath().toString();
con's avatar
con committed
228

hjk's avatar
hjk committed
229
    return d->recursiveDependencyCheck(newDep, checkDep);
con's avatar
con committed
230 231
}

dt's avatar
dt committed
232
bool SessionManager::addDependency(Project *project, Project *depProject)
con's avatar
con committed
233
{
234 235
    const QString proName = project->projectFilePath().toString();
    const QString depName = depProject->projectFilePath().toString();
con's avatar
con committed
236 237

    // check if this dependency is valid
hjk's avatar
hjk committed
238
    if (!d->recursiveDependencyCheck(proName, depName))
con's avatar
con committed
239 240
        return false;

hjk's avatar
hjk committed
241
    QStringList proDeps = d->m_depMap.value(proName);
con's avatar
con committed
242 243
    if (!proDeps.contains(depName)) {
        proDeps.append(depName);
hjk's avatar
hjk committed
244
        d->m_depMap[proName] = proDeps;
con's avatar
con committed
245
    }
hjk's avatar
hjk committed
246
    emit m_instance->dependencyChanged(project, depProject);
con's avatar
con committed
247 248 249 250

    return true;
}

dt's avatar
dt committed
251
void SessionManager::removeDependency(Project *project, Project *depProject)
252
{
253 254
    const QString proName = project->projectFilePath().toString();
    const QString depName = depProject->projectFilePath().toString();
255

hjk's avatar
hjk committed
256
    QStringList proDeps = d->m_depMap.value(proName);
257
    proDeps.removeAll(depName);
258
    if (proDeps.isEmpty())
hjk's avatar
hjk committed
259
        d->m_depMap.remove(proName);
260
    else
hjk's avatar
hjk committed
261 262
        d->m_depMap[proName] = proDeps;
    emit m_instance->dependencyChanged(project, depProject);
263 264
}

con's avatar
con committed
265 266 267
void SessionManager::setStartupProject(Project *startupProject)
{
    if (debug)
268
        qDebug() << Q_FUNC_INFO << (startupProject ? startupProject->displayName() : QLatin1String("0"));
con's avatar
con committed
269 270

    if (startupProject) {
hjk's avatar
hjk committed
271
        Q_ASSERT(d->m_projects.contains(startupProject));
con's avatar
con committed
272 273
    }

hjk's avatar
hjk committed
274
    if (d->m_startupProject == startupProject)
275 276
        return;

hjk's avatar
hjk committed
277 278
    d->m_startupProject = startupProject;
    emit m_instance->startupProjectChanged(startupProject);
con's avatar
con committed
279 280
}

hjk's avatar
hjk committed
281
Project *SessionManager::startupProject()
con's avatar
con committed
282
{
hjk's avatar
hjk committed
283
    return d->m_startupProject;
con's avatar
con committed
284 285 286 287 288 289 290 291 292
}

void SessionManager::addProject(Project *project)
{
    addProjects(QList<Project*>() << project);
}

void SessionManager::addProjects(const QList<Project*> &projects)
{
hjk's avatar
hjk committed
293
    d->m_virginSession = false;
con's avatar
con committed
294 295
    QList<Project*> clearedList;
    foreach (Project *pro, projects) {
hjk's avatar
hjk committed
296
        if (!d->m_projects.contains(pro)) {
con's avatar
con committed
297
            clearedList.append(pro);
hjk's avatar
hjk committed
298 299
            d->m_projects.append(pro);
            d->m_sessionNode->addProjectNodes(QList<ProjectNode *>() << pro->rootProjectNode());
con's avatar
con committed
300 301

            connect(pro, SIGNAL(fileListChanged()),
hjk's avatar
hjk committed
302
                    m_instance, SLOT(clearProjectFileCache()));
con's avatar
con committed
303

304
            connect(pro, SIGNAL(displayNameChanged()),
hjk's avatar
hjk committed
305
                    m_instance, SLOT(projectDisplayNameChanged()));
306

con's avatar
con committed
307
            if (debug)
308
                qDebug() << "SessionManager - adding project " << pro->displayName();
con's avatar
con committed
309 310 311
        }
    }

hjk's avatar
hjk committed
312 313
    foreach (Project *pro, clearedList)
        emit m_instance->projectAdded(pro);
con's avatar
con committed
314 315

    if (clearedList.count() == 1)
hjk's avatar
hjk committed
316
        emit m_instance->singleProjectAdded(clearedList.first());
con's avatar
con committed
317 318 319 320
}

void SessionManager::removeProject(Project *project)
{
hjk's avatar
hjk committed
321
    d->m_virginSession = false;
con's avatar
con committed
322 323 324 325 326 327 328
    if (project == 0) {
        qDebug() << "SessionManager::removeProject(0) ... THIS SHOULD NOT HAPPEN";
        return;
    }
    removeProjects(QList<Project*>() << project);
}

329 330
bool SessionManager::loadingSession()
{
hjk's avatar
hjk committed
331
    return d->m_loadingSession;
332 333
}

con's avatar
con committed
334 335 336
bool SessionManager::save()
{
    if (debug)
hjk's avatar
hjk committed
337
        qDebug() << "SessionManager - saving session" << d->m_sessionName;
con's avatar
con committed
338

hjk's avatar
hjk committed
339
    emit m_instance->aboutToSaveSession();
con's avatar
con committed
340

hjk's avatar
hjk committed
341 342 343
    if (!d->m_writer || d->m_writer->fileName() != sessionNameToFileName(d->m_sessionName)) {
        delete d->m_writer;
        d->m_writer = new PersistentSettingsWriter(sessionNameToFileName(d->m_sessionName),
Tobias Hunger's avatar
Tobias Hunger committed
344 345
                                                       QLatin1String("QtCreatorSession"));
    }
346

347
    QVariantMap data;
348
    // save the startup project
hjk's avatar
hjk committed
349
    if (d->m_startupProject)
350
        data.insert(QLatin1String("StartupProject"), d->m_startupProject->projectFilePath().toString());
351

hjk's avatar
hjk committed
352
    QColor c = StyleHelper::requestedBaseColor();
353 354 355 356 357
    if (c.isValid()) {
        QString tmp = QString::fromLatin1("#%1%2%3")
                .arg(c.red(), 2, 16, QLatin1Char('0'))
                .arg(c.green(), 2, 16, QLatin1Char('0'))
                .arg(c.blue(), 2, 16, QLatin1Char('0'));
358
        data.insert(QLatin1String("Color"), tmp);
359 360
    }

361
    QStringList projectFiles;
hjk's avatar
hjk committed
362
    foreach (Project *pro, d->m_projects)
363
        projectFiles << pro->projectFilePath().toString();
364 365

    // Restore infromation on projects that failed to load:
366
    // don't readd projects to the list, which the user loaded
hjk's avatar
hjk committed
367
    foreach (const QString &failed, d->m_failedProjects)
368 369
        if (!projectFiles.contains(failed))
            projectFiles << failed;
370

371
    data.insert(QLatin1String("ProjectList"), projectFiles);
372 373

    QMap<QString, QVariant> depMap;
hjk's avatar
hjk committed
374 375
    QMap<QString, QStringList>::const_iterator i = d->m_depMap.constBegin();
    while (i != d->m_depMap.constEnd()) {
376 377 378 379 380 381 382 383
        QString key = i.key();
        QStringList values;
        foreach (const QString &value, i.value()) {
            values << value;
        }
        depMap.insert(key, values);
        ++i;
    }
384
    data.insert(QLatin1String("ProjectDependencies"), QVariant(depMap));
385
    data.insert(QLatin1String("EditorSettings"), EditorManager::saveState().toBase64());
386

hjk's avatar
hjk committed
387
    QMap<QString, QVariant>::const_iterator it, end = d->m_values.constEnd();
388
    QStringList keys;
hjk's avatar
hjk committed
389
    for (it = d->m_values.constBegin(); it != end; ++it) {
390
        data.insert(QLatin1String("value-") + it.key(), it.value());
391 392 393
        keys << it.key();
    }

394
    data.insert(QLatin1String("valueKeys"), keys);
395

hjk's avatar
hjk committed
396
    bool result = d->m_writer->save(data, Core::ICore::mainWindow());
con's avatar
con committed
397
    if (!result) {
398
        QMessageBox::warning(ICore::dialogParent(), tr("Error while saving session"),
hjk's avatar
hjk committed
399
            tr("Could not save session to file %1").arg(d->m_writer->fileName().toUserOutput()));
con's avatar
con committed
400 401 402 403 404 405 406 407 408
    }

    if (debug)
        qDebug() << "SessionManager - saving session returned " << result;

    return result;
}

/*!
409
  Closes all projects
con's avatar
con committed
410
  */
411
void SessionManager::closeAllProjects()
con's avatar
con committed
412
{
413 414
    setStartupProject(0);
    removeProjects(projects());
con's avatar
con committed
415 416
}

hjk's avatar
hjk committed
417 418 419 420 421 422
const QList<Project *> &SessionManager::projects()
{
    return d->m_projects;
}

bool SessionManager::hasProjects()
con's avatar
con committed
423
{
hjk's avatar
hjk committed
424
    return !d->m_projects.isEmpty();
con's avatar
con committed
425 426
}

hjk's avatar
hjk committed
427
QStringList SessionManagerPrivate::dependencies(const QString &proName) const
con's avatar
con committed
428 429
{
    QStringList result;
430
    dependencies(proName, result);
con's avatar
con committed
431 432 433
    return result;
}

hjk's avatar
hjk committed
434
void SessionManagerPrivate::dependencies(const QString &proName, QStringList &result) const
435 436 437 438 439 440 441 442 443 444
{
    QStringList depends = m_depMap.value(proName);

    foreach (const QString &dep, depends)
        dependencies(dep, result);

    if (!result.contains(proName))
        result.append(proName);
}

hjk's avatar
hjk committed
445
QStringList SessionManagerPrivate::dependenciesOrder() const
con's avatar
con committed
446 447 448 449 450
{
    QList<QPair<QString, QStringList> > unordered;
    QStringList ordered;

    // copy the map to a temporary list
hjk's avatar
hjk committed
451
    foreach (Project *pro, m_projects) {
452
        const QString &proName = pro->projectFilePath().toString();
hjk's avatar
hjk committed
453
        unordered << QPair<QString, QStringList>(proName, m_depMap.value(proName));
con's avatar
con committed
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
    }

    while (!unordered.isEmpty()) {
        for (int i=(unordered.count()-1); i>=0; --i) {
            if (unordered.at(i).second.isEmpty()) {
                ordered << unordered.at(i).first;
                unordered.removeAt(i);
            }
        }

        // remove the handled projects from the dependency lists
        // of the remaining unordered projects
        for (int i = 0; i < unordered.count(); ++i) {
            foreach (const QString &pro, ordered) {
                QStringList depList = unordered.at(i).second;
                depList.removeAll(pro);
                unordered[i].second = depList;
            }
        }
    }

    return ordered;
}

hjk's avatar
hjk committed
478
QList<Project *> SessionManager::projectOrder(Project *project)
con's avatar
con committed
479 480 481 482
{
    QList<Project *> result;

    QStringList pros;
483
    if (project)
484
        pros = d->dependencies(project->projectFilePath().toString());
485
    else
hjk's avatar
hjk committed
486
        pros = d->dependenciesOrder();
con's avatar
con committed
487 488 489

    foreach (const QString &proFile, pros) {
        foreach (Project *pro, projects()) {
490
            if (pro->projectFilePath().toString() == proFile) {
con's avatar
con committed
491 492 493 494 495 496 497 498 499
                result << pro;
                break;
            }
        }
    }

    return result;
}

hjk's avatar
hjk committed
500
Project *SessionManager::projectForNode(Node *node)
con's avatar
con committed
501 502 503 504 505 506 507
{
    if (!node)
        return 0;

    FolderNode *rootProjectNode = qobject_cast<FolderNode*>(node);
    if (!rootProjectNode)
        rootProjectNode = node->parentFolderNode();
hjk's avatar
hjk committed
508 509

    while (rootProjectNode && rootProjectNode->parentFolderNode() != d->m_sessionNode)
con's avatar
con committed
510 511
        rootProjectNode = rootProjectNode->parentFolderNode();

dt's avatar
dt committed
512
    Q_ASSERT(rootProjectNode);
con's avatar
con committed
513

hjk's avatar
hjk committed
514 515 516
    foreach (Project *p, d->m_projects)
        if (p->rootProjectNode() == rootProjectNode)
            return p;
con's avatar
con committed
517

hjk's avatar
hjk committed
518
    return 0;
con's avatar
con committed
519 520
}

hjk's avatar
hjk committed
521
Node *SessionManager::nodeForFile(const QString &fileName, Project *project)
con's avatar
con committed
522 523
{
    Node *node = 0;
dt's avatar
dt committed
524 525
    if (!project)
        project = projectForFile(fileName);
hjk's avatar
hjk committed
526

dt's avatar
dt committed
527
    if (project) {
con's avatar
con committed
528 529 530 531 532 533 534 535 536 537 538 539 540
        FindNodesForFileVisitor findNodes(fileName);
        project->rootProjectNode()->accept(&findNodes);

        foreach (Node *n, findNodes.nodes()) {
            // prefer file nodes
            if (!node || (node->nodeType() != FileNodeType && n->nodeType() == FileNodeType))
                node = n;
        }
    }

    return node;
}

hjk's avatar
hjk committed
541
Project *SessionManager::projectForFile(const QString &fileName)
con's avatar
con committed
542 543 544 545
{
    if (debug)
        qDebug() << "SessionManager::projectForFile(" << fileName << ")";

546
    const QList<Project *> &projectList = projects();
con's avatar
con committed
547

548
    // Check current project first
549
    Project *currentProject = ProjectExplorerPlugin::currentProject();
hjk's avatar
hjk committed
550
    if (currentProject && d->projectContainsFile(currentProject, fileName))
551
        return currentProject;
con's avatar
con committed
552

553
    foreach (Project *p, projectList)
hjk's avatar
hjk committed
554
        if (p != currentProject && d->projectContainsFile(p, fileName))
555
            return p;
hjk's avatar
hjk committed
556

557 558 559
    return 0;
}

hjk's avatar
hjk committed
560
bool SessionManagerPrivate::projectContainsFile(Project *p, const QString &fileName) const
561 562 563 564 565
{
    if (!m_projectFileCache.contains(p))
        m_projectFileCache.insert(p, p->files(Project::AllFiles));

    return m_projectFileCache.value(p).contains(fileName);
con's avatar
con committed
566 567
}

568
void SessionManager::configureEditor(Core::IEditor *editor, const QString &fileName)
con's avatar
con committed
569
{
570 571 572
    if (TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor)) {
        Project *project = projectForFile(fileName);
        // Global settings are the default.
573 574
        if (project)
            project->editorConfiguration()->configureEditor(textEditor);
575
    }
con's avatar
con committed
576 577 578 579
}

void SessionManager::updateWindowTitle()
{
hjk's avatar
hjk committed
580
    if (isDefaultSession(d->m_sessionName)) {
581
        if (Project *currentProject = ProjectExplorerPlugin::currentProject())
582
            EditorManager::setWindowTitleAddition(currentProject->displayName());
583
        else
584
            EditorManager::setWindowTitleAddition(QString());
dt's avatar
dt committed
585
    } else {
hjk's avatar
hjk committed
586
        QString sessionName = d->m_sessionName;
con's avatar
con committed
587 588
        if (sessionName.isEmpty())
            sessionName = tr("Untitled");
589
        EditorManager::setWindowTitleAddition(sessionName);
590
    }
con's avatar
con committed
591 592 593 594 595 596 597 598
}

void SessionManager::removeProjects(QList<Project *> remove)
{
    QMap<QString, QStringList> resMap;

    foreach (Project *pro, remove) {
        if (debug)
599
            qDebug() << "SessionManager - emitting aboutToRemoveProject(" << pro->displayName() << ")";
hjk's avatar
hjk committed
600
        emit m_instance->aboutToRemoveProject(pro);
con's avatar
con committed
601 602 603 604 605 606 607
    }


    // Refresh dependencies
    QSet<QString> projectFiles;
    foreach (Project *pro, projects()) {
        if (!remove.contains(pro))
608
            projectFiles.insert(pro->projectFilePath().toString());
con's avatar
con committed
609 610 611 612 613
    }

    QSet<QString>::const_iterator i = projectFiles.begin();
    while (i != projectFiles.end()) {
        QStringList dependencies;
hjk's avatar
hjk committed
614
        foreach (const QString &dependency, d->m_depMap.value(*i)) {
con's avatar
con committed
615 616 617 618 619 620 621 622
            if (projectFiles.contains(dependency))
                dependencies << dependency;
        }
        if (!dependencies.isEmpty())
            resMap.insert(*i, dependencies);
        ++i;
    }

hjk's avatar
hjk committed
623
    d->m_depMap = resMap;
con's avatar
con committed
624

625 626 627 628 629
    // TODO: Clear m_modelProjectHash

    // Delete projects
    foreach (Project *pro, remove) {
        pro->saveSettings();
hjk's avatar
hjk committed
630
        d->m_projects.removeOne(pro);
631

hjk's avatar
hjk committed
632
        if (pro == d->m_startupProject)
633 634
            setStartupProject(0);

hjk's avatar
hjk committed
635 636
        disconnect(pro, SIGNAL(fileListChanged()), m_instance, SLOT(clearProjectFileCache()));
        d->m_projectFileCache.remove(pro);
637 638 639

        if (debug)
            qDebug() << "SessionManager - emitting projectRemoved(" << pro->displayName() << ")";
hjk's avatar
hjk committed
640 641
        d->m_sessionNode->removeProjectNodes(QList<ProjectNode *>() << pro->rootProjectNode());
        emit m_instance->projectRemoved(pro);
642 643 644
        delete pro;
    }

con's avatar
con committed
645
    if (startupProject() == 0)
hjk's avatar
hjk committed
646 647
        if (!d->m_projects.isEmpty())
            setStartupProject(d->m_projects.first());
con's avatar
con committed
648 649
}

650
/*!
651
    Lets other plugins store persistent values within the session file.
652 653
*/

con's avatar
con committed
654 655
void SessionManager::setValue(const QString &name, const QVariant &value)
{
hjk's avatar
hjk committed
656
    if (d->m_values.value(name) == value)
657
        return;
hjk's avatar
hjk committed
658
    d->m_values.insert(name, value);
659
    markSessionFileDirty(false);
con's avatar
con committed
660 661 662 663
}

QVariant SessionManager::value(const QString &name)
{
hjk's avatar
hjk committed
664 665
    QMap<QString, QVariant>::const_iterator it = d->m_values.find(name);
    return (it == d->m_values.constEnd()) ? QVariant() : *it;
con's avatar
con committed
666 667
}

hjk's avatar
hjk committed
668
QString SessionManager::activeSession()
con's avatar
con committed
669
{
hjk's avatar
hjk committed
670
    return d->m_sessionName;
con's avatar
con committed
671 672
}

hjk's avatar
hjk committed
673
QStringList SessionManager::sessions()
con's avatar
con committed
674
{
hjk's avatar
hjk committed
675
    if (d->m_sessions.isEmpty()) {
Tobias Hunger's avatar
Tobias Hunger committed
676
        // We are not initialized yet, so do that now
hjk's avatar
hjk committed
677
        QDir sessionDir(Core::ICore::userResourcePath());
678
        QList<QFileInfo> sessionFiles = sessionDir.entryInfoList(QStringList() << QLatin1String("*.qws"), QDir::NoFilter, QDir::Time);
hjk's avatar
hjk committed
679
        foreach (const QFileInfo &fileInfo, sessionFiles) {
680
            if (fileInfo.completeBaseName() != QLatin1String("default"))
hjk's avatar
hjk committed
681
                d->m_sessions << fileInfo.completeBaseName();
682
        }
hjk's avatar
hjk committed
683
        d->m_sessions.prepend(QLatin1String("default"));
684
    }
hjk's avatar
hjk committed
685
    return d->m_sessions;
con's avatar
con committed
686 687
}

hjk's avatar
hjk committed
688
FileName SessionManager::sessionNameToFileName(const QString &session)
con's avatar
con committed
689
{
hjk's avatar
hjk committed
690
    return FileName::fromString(ICore::userResourcePath() + QLatin1Char('/') + session + QLatin1String(".qws"));
con's avatar
con committed
691 692
}

693
/*!
694
    Creates \a session, but does not actually create the file.
695 696
*/

con's avatar
con committed
697 698 699 700
bool SessionManager::createSession(const QString &session)
{
    if (sessions().contains(session))
        return false;
hjk's avatar
hjk committed
701 702
    Q_ASSERT(d->m_sessions.size() > 0);
    d->m_sessions.insert(1, session);
con's avatar
con committed
703 704 705
    return true;
}

706 707 708 709 710 711 712 713 714
bool SessionManager::renameSession(const QString &original, const QString &newName)
{
    if (!cloneSession(original, newName))
        return false;
    if (original == activeSession())
        loadSession(newName);
    return deleteSession(original);
}

715 716 717 718 719 720 721 722 723 724 725 726

/*!
    \brief Shows a dialog asking the user to confirm deleting the session \p session
*/
bool SessionManager::confirmSessionDelete(const QString &session)
{
    return QMessageBox::question(Core::ICore::mainWindow(),
                                 tr("Delete Session"),
                                 tr("Delete session %1?").arg(session),
                                 QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes;
}

727
/*!
728
     Deletes \a session name from session list and the file from disk.
729
*/
con's avatar
con committed
730 731
bool SessionManager::deleteSession(const QString &session)
{
hjk's avatar
hjk committed
732
    if (!d->m_sessions.contains(session))
con's avatar
con committed
733
        return false;
hjk's avatar
hjk committed
734
    d->m_sessions.removeOne(session);
735
    QFile fi(sessionNameToFileName(session).toString());
con's avatar
con committed
736 737 738 739 740 741 742
    if (fi.exists())
        return fi.remove();
    return false;
}

bool SessionManager::cloneSession(const QString &original, const QString &clone)
{
hjk's avatar
hjk committed
743
    if (!d->m_sessions.contains(original))
con's avatar
con committed
744 745
        return false;

746
    QFile fi(sessionNameToFileName(original).toString());
con's avatar
con committed
747
    // If the file does not exist, we can still clone
748
    if (!fi.exists() || fi.copy(sessionNameToFileName(clone).toString())) {
hjk's avatar
hjk committed
749 750
        Q_ASSERT(d->m_sessions.size() > 0);
        d->m_sessions.insert(1, clone);
con's avatar
con committed
751 752 753 754 755
        return true;
    }
    return false;
}

hjk's avatar
hjk committed
756
void SessionManagerPrivate::restoreValues(const PersistentSettingsReader &reader)
757
{
hjk's avatar
hjk committed
758
    const QStringList keys = reader.restoreValue(QLatin1String("valueKeys")).toStringList();
759 760 761 762 763 764
    foreach (const QString &key, keys) {
        QVariant value = reader.restoreValue(QLatin1String("value-") + key);
        m_values.insert(key, value);
    }
}

hjk's avatar
hjk committed
765
void SessionManagerPrivate::restoreDependencies(const PersistentSettingsReader &reader)
766 767 768 769 770
{
    QMap<QString, QVariant> depMap = reader.restoreValue(QLatin1String("ProjectDependencies")).toMap();
    QMap<QString, QVariant>::const_iterator i = depMap.constBegin();
    while (i != depMap.constEnd()) {
        const QString &key = i.key();
771 772 773 774 775 776 777
        if (!m_failedProjects.contains(key)) {
            QStringList values;
            foreach (const QString &value, i.value().toStringList()) {
                if (!m_failedProjects.contains(value))
                    values << value;
            }
            m_depMap.insert(key, values);
778 779 780 781 782
        }
        ++i;
    }
}

hjk's avatar
hjk committed
783
void SessionManagerPrivate::askUserAboutFailedProjects()
784 785 786 787 788 789
{
    QStringList failedProjects = m_failedProjects;
    if (!failedProjects.isEmpty()) {
        QString fileList =
            QDir::toNativeSeparators(failedProjects.join(QLatin1String("<br>")));
        QMessageBox * box = new QMessageBox(QMessageBox::Warning,
hjk's avatar
hjk committed
790 791
                                            SessionManager::tr("Failed to restore project files"),
                                            SessionManager::tr("Could not restore the following project files:<br><b>%1</b>").
792
                                            arg(fileList));
hjk's avatar
hjk committed
793 794
        QPushButton * keepButton = new QPushButton(SessionManager::tr("Keep projects in Session"), box);
        QPushButton * removeButton = new QPushButton(SessionManager::tr("Remove projects from Session"), box);
795 796 797 798 799 800 801 802 803 804
        box->addButton(keepButton, QMessageBox::AcceptRole);
        box->addButton(removeButton, QMessageBox::DestructiveRole);

        box->exec();

        if (box->clickedButton() == removeButton)
            m_failedProjects.clear();
    }
}

hjk's avatar
hjk committed
805
void SessionManagerPrivate::restoreStartupProject(const PersistentSettingsReader &reader)
806 807 808
{
    const QString startupProject = reader.restoreValue(QLatin1String("StartupProject")).toString();
    if (!startupProject.isEmpty()) {
hjk's avatar
hjk committed
809
        foreach (Project *pro, d->m_projects) {
810
            if (pro->projectFilePath().toString() == startupProject) {
hjk's avatar
hjk committed
811
                m_instance->setStartupProject(pro);
812 813 814
                break;
            }
        }
815 816
    }
    if (!m_startupProject) {
817 818
        if (!startupProject.isEmpty())
            qWarning() << "Could not find startup project" << startupProject;
hjk's avatar
hjk committed
819 820
        if (!m_projects.isEmpty())
            m_instance->setStartupProject(m_projects.first());
821 822 823
    }
}

hjk's avatar
hjk committed
824
void SessionManagerPrivate::restoreEditors(const PersistentSettingsReader &reader)
825
{
hjk's avatar
hjk committed
826
    const QVariant editorsettings = reader.restoreValue(QLatin1String("EditorSettings"));
827
    if (editorsettings.isValid()) {
828
        EditorManager::restoreState(QByteArray::fromBase64(editorsettings.toByteArray()));
829
        sessionLoadingProgress();
830 831 832
    }
}

833
/*!
834
     Loads a session, takes a session name (not filename).
835
*/
hjk's avatar
hjk committed
836
void SessionManagerPrivate::restoreProjects(const QStringList &fileList)
837 838 839 840 841 842 843 844
{
    // indirectly adds projects to session
    // Keep projects that failed to load in the session!
    m_failedProjects = fileList;
    if (!fileList.isEmpty()) {
        QString errors;
        QList<Project *> projects = ProjectExplorerPlugin::instance()->openProjects(fileList, &errors);
        if (!errors.isEmpty())
hjk's avatar
hjk committed
845
            QMessageBox::critical(Core::ICore::mainWindow(), SessionManager::tr("Failed to open project"), errors);
846
        foreach (Project *p, projects)
847
            m_failedProjects.removeAll(p->projectFilePath().toString());
848 849
    }
}
850

con's avatar
con committed
851 852 853 854 855
bool SessionManager::loadSession(const QString &session)
{
    // Do nothing if we have that session already loaded,
    // exception if the session is the default virgin session
    // we still want to be able to load the default session
hjk's avatar
hjk committed
856
    if (session == d->m_sessionName && !isDefaultVirgin())
con's avatar
con committed
857 858 859 860
        return true;

    if (!sessions().contains(session))
        return false;
861

862
    // Try loading the file
hjk's avatar
hjk committed
863
    FileName fileName = sessionNameToFileName(session);
864
    PersistentSettingsReader reader;
865
    if (fileName.toFileInfo().exists()) {
866
        if (!reader.load(fileName)) {
867
            QMessageBox::warning(ICore::dialogParent(), tr("Error while restoring session"),
868
                                 tr("Could not restore session %1").arg(fileName.toUserOutput()));
869 870 871 872
            return false;
        }
    }

hjk's avatar
hjk committed
873
    d->m_loadingSession = true;
874

875
    // Allow everyone to set something in the session and before saving
hjk's avatar
hjk committed
876
    emit m_instance->aboutToUnloadSession(d->m_sessionName);
877 878

    if (!isDefaultVirgin()) {
879
        if (!save()) {
hjk's avatar
hjk committed
880
            d->m_loadingSession = false;
881
            return false;
882
        }
883 884 885
    }

    // Clean up
886
    if (!EditorManager::closeAllEditors()) {
hjk's avatar
hjk committed
887
        d->m_loadingSession = false;
888
        return false;