1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "qtversionmanager.h"
27
28 #include "baseqtversion.h"
29 #include "exampleslistmodel.h"
30 #include "qtkitinformation.h"
31 #include "qtsupportconstants.h"
32 #include "qtversionfactory.h"
33
34 #include <coreplugin/icore.h>
35 #include <coreplugin/helpmanager.h>
36
37 #include <extensionsystem/pluginmanager.h>
38
39 #include <projectexplorer/toolchainmanager.h>
40
41 #include <utils/algorithm.h>
42 #include <utils/buildablehelperlibrary.h>
43 #include <utils/environment.h>
44 #include <utils/filesystemwatcher.h>
45 #include <utils/hostosinfo.h>
46 #include <utils/persistentsettings.h>
47 #include <utils/qtcprocess.h>
48 #include <utils/qtcassert.h>
49
50 #include <QDir>
51 #include <QFile>
52 #include <QLoggingCategory>
53 #include <QSettings>
54 #include <QStandardPaths>
55 #include <QStringList>
56 #include <QTextStream>
57 #include <QTimer>
58
59 using namespace Utils;
60
61 namespace QtSupport {
62
63 using namespace Internal;
64
65 const char QTVERSION_DATA_KEY[] = "QtVersion.";
66 const char QTVERSION_TYPE_KEY[] = "QtVersion.Type";
67 const char QTVERSION_FILE_VERSION_KEY[] = "Version";
68 const char QTVERSION_FILENAME[] = "qtversion.xml";
69
70 using VersionMap = QMap<int, BaseQtVersion *>;
71 static VersionMap m_versions;
72
73 const char DOCUMENTATION_SETTING_KEY[] = "QtSupport/DocumentationSetting";
74
75 static int m_idcount = 0;
76 // managed by QtProjectManagerPlugin
77 static QtVersionManager *m_instance = nullptr;
78 static FileSystemWatcher *m_configFileWatcher = nullptr;
79 static QTimer *m_fileWatcherTimer = nullptr;
80 static PersistentSettingsWriter *m_writer = nullptr;
81 static QVector<ExampleSetModel::ExtraExampleSet> m_pluginRegisteredExampleSets;
82
83 static Q_LOGGING_CATEGORY(log, "qtc.qt.versions", QtWarningMsg);
84
globalSettingsFileName()85 static FilePath globalSettingsFileName()
86 {
87 return Core::ICore::installerResourcePath(QTVERSION_FILENAME);
88 }
89
settingsFileName(const QString & path)90 static FilePath settingsFileName(const QString &path)
91 {
92 return Core::ICore::userResourcePath(path);
93 }
94
95
96 // prefer newer qts otherwise compare on id
qtVersionNumberCompare(BaseQtVersion * a,BaseQtVersion * b)97 bool qtVersionNumberCompare(BaseQtVersion *a, BaseQtVersion *b)
98 {
99 return a->qtVersion() > b->qtVersion() || (a->qtVersion() == b->qtVersion() && a->uniqueId() < b->uniqueId());
100 }
101 static bool restoreQtVersions();
102 static void findSystemQt();
103 static void saveQtVersions();
104
pluginRegisteredExampleSets()105 QVector<ExampleSetModel::ExtraExampleSet> ExampleSetModel::pluginRegisteredExampleSets()
106 {
107 return m_pluginRegisteredExampleSets;
108 }
109
110 // --------------------------------------------------------------------------
111 // QtVersionManager
112 // --------------------------------------------------------------------------
113
QtVersionManager()114 QtVersionManager::QtVersionManager()
115 {
116 m_instance = this;
117 m_configFileWatcher = nullptr;
118 m_fileWatcherTimer = new QTimer(this);
119 m_writer = nullptr;
120 m_idcount = 1;
121
122 qRegisterMetaType<FilePath>();
123
124 // Give the file a bit of time to settle before reading it...
125 m_fileWatcherTimer->setInterval(2000);
126 connect(m_fileWatcherTimer, &QTimer::timeout, this, [this] { updateFromInstaller(); });
127 }
128
triggerQtVersionRestore()129 void QtVersionManager::triggerQtVersionRestore()
130 {
131 disconnect(ProjectExplorer::ToolChainManager::instance(), &ProjectExplorer::ToolChainManager::toolChainsLoaded,
132 this, &QtVersionManager::triggerQtVersionRestore);
133
134 bool success = restoreQtVersions();
135 m_instance->updateFromInstaller(false);
136 if (!success) {
137 // We did neither restore our settings or upgraded
138 // in that case figure out if there's a qt in path
139 // and add it to the Qt versions
140 findSystemQt();
141 }
142
143 emit m_instance->qtVersionsLoaded();
144 emit m_instance->qtVersionsChanged(m_versions.keys(), QList<int>(), QList<int>());
145 saveQtVersions();
146
147 const FilePath configFileName = globalSettingsFileName();
148 if (configFileName.exists()) {
149 m_configFileWatcher = new FileSystemWatcher(m_instance);
150 connect(m_configFileWatcher, &FileSystemWatcher::fileChanged,
151 m_fileWatcherTimer, QOverload<>::of(&QTimer::start));
152 m_configFileWatcher->addFile(configFileName.toString(),
153 FileSystemWatcher::WatchModifiedDate);
154 } // exists
155
156 const QList<BaseQtVersion *> vs = versions();
157 updateDocumentation(vs, {}, vs);
158 }
159
isLoaded()160 bool QtVersionManager::isLoaded()
161 {
162 return m_writer;
163 }
164
~QtVersionManager()165 QtVersionManager::~QtVersionManager()
166 {
167 delete m_writer;
168 qDeleteAll(m_versions);
169 m_versions.clear();
170 }
171
initialized()172 void QtVersionManager::initialized()
173 {
174 connect(ProjectExplorer::ToolChainManager::instance(), &ProjectExplorer::ToolChainManager::toolChainsLoaded,
175 QtVersionManager::instance(), &QtVersionManager::triggerQtVersionRestore);
176 }
177
instance()178 QtVersionManager *QtVersionManager::instance()
179 {
180 return m_instance;
181 }
182
restoreQtVersions()183 static bool restoreQtVersions()
184 {
185 QTC_ASSERT(!m_writer, return false);
186 m_writer = new PersistentSettingsWriter(settingsFileName(QTVERSION_FILENAME),
187 "QtCreatorQtVersions");
188
189 const QList<QtVersionFactory *> factories = QtVersionFactory::allQtVersionFactories();
190
191 PersistentSettingsReader reader;
192 const FilePath filename = settingsFileName(QTVERSION_FILENAME);
193
194 if (!reader.load(filename))
195 return false;
196 QVariantMap data = reader.restoreValues();
197
198 // Check version:
199 const int version = data.value(QTVERSION_FILE_VERSION_KEY, 0).toInt();
200 if (version < 1)
201 return false;
202
203 const QString keyPrefix(QTVERSION_DATA_KEY);
204 const QVariantMap::ConstIterator dcend = data.constEnd();
205 for (QVariantMap::ConstIterator it = data.constBegin(); it != dcend; ++it) {
206 const QString &key = it.key();
207 if (!key.startsWith(keyPrefix))
208 continue;
209 bool ok;
210 int count = key.mid(keyPrefix.count()).toInt(&ok);
211 if (!ok || count < 0)
212 continue;
213
214 const QVariantMap qtversionMap = it.value().toMap();
215 const QString type = qtversionMap.value(QTVERSION_TYPE_KEY).toString();
216
217 bool restored = false;
218 for (QtVersionFactory *f : factories) {
219 if (f->canRestore(type)) {
220 if (BaseQtVersion *qtv = f->restore(type, qtversionMap)) {
221 if (m_versions.contains(qtv->uniqueId())) {
222 // This shouldn't happen, we are restoring the same id multiple times?
223 qWarning() << "A Qt version with id"<<qtv->uniqueId()<<"already exists";
224 delete qtv;
225 } else {
226 m_versions.insert(qtv->uniqueId(), qtv);
227 m_idcount = qtv->uniqueId() > m_idcount ? qtv->uniqueId() : m_idcount;
228 restored = true;
229 break;
230 }
231 }
232 }
233 }
234 if (!restored)
235 qWarning("Warning: Unable to restore Qt version '%s' stored in %s.",
236 qPrintable(type),
237 qPrintable(filename.toUserOutput()));
238 }
239 ++m_idcount;
240
241 return true;
242 }
243
updateFromInstaller(bool emitSignal)244 void QtVersionManager::updateFromInstaller(bool emitSignal)
245 {
246 m_fileWatcherTimer->stop();
247
248 const FilePath path = globalSettingsFileName();
249 // Handle overwritting of data:
250 if (m_configFileWatcher) {
251 m_configFileWatcher->removeFile(path.toString());
252 m_configFileWatcher->addFile(path.toString(), FileSystemWatcher::WatchModifiedDate);
253 }
254
255 QList<int> added;
256 QList<int> removed;
257 QList<int> changed;
258
259 const QList<QtVersionFactory *> factories = QtVersionFactory::allQtVersionFactories();
260 PersistentSettingsReader reader;
261 QVariantMap data;
262 if (reader.load(path))
263 data = reader.restoreValues();
264
265 if (log().isDebugEnabled()) {
266 qCDebug(log) << "======= Existing Qt versions =======";
267 for (BaseQtVersion *version : qAsConst(m_versions)) {
268 qCDebug(log) << version->qmakeFilePath().toUserOutput() << "id:"<<version->uniqueId();
269 qCDebug(log) << " autodetection source:" << version->detectionSource();
270 qCDebug(log) << "";
271 }
272 qCDebug(log)<< "======= Adding sdk versions =======";
273 }
274
275 QStringList sdkVersions;
276
277 const QString keyPrefix(QTVERSION_DATA_KEY);
278 const QVariantMap::ConstIterator dcend = data.constEnd();
279 for (QVariantMap::ConstIterator it = data.constBegin(); it != dcend; ++it) {
280 const QString &key = it.key();
281 if (!key.startsWith(keyPrefix))
282 continue;
283 bool ok;
284 int count = key.mid(keyPrefix.count()).toInt(&ok);
285 if (!ok || count < 0)
286 continue;
287
288 QVariantMap qtversionMap = it.value().toMap();
289 const QString type = qtversionMap.value(QTVERSION_TYPE_KEY).toString();
290 const QString autoDetectionSource = qtversionMap.value("autodetectionSource").toString();
291 sdkVersions << autoDetectionSource;
292 int id = -1; // see BaseQtVersion::fromMap()
293 QtVersionFactory *factory = nullptr;
294 for (QtVersionFactory *f : factories) {
295 if (f->canRestore(type))
296 factory = f;
297 }
298 if (!factory) {
299 qCDebug(log, "Warning: Unable to find factory for type '%s'", qPrintable(type));
300 continue;
301 }
302 // First try to find a existing Qt version to update
303 bool restored = false;
304 const VersionMap versionsCopy = m_versions; // m_versions is modified in loop
305 for (BaseQtVersion *v : versionsCopy) {
306 if (v->detectionSource() == autoDetectionSource) {
307 id = v->uniqueId();
308 qCDebug(log) << " Qt version found with same autodetection source" << autoDetectionSource << " => Migrating id:" << id;
309 m_versions.remove(id);
310 qtversionMap[Constants::QTVERSIONID] = id;
311 qtversionMap[Constants::QTVERSIONNAME] = v->unexpandedDisplayName();
312 delete v;
313
314 if (BaseQtVersion *qtv = factory->restore(type, qtversionMap)) {
315 Q_ASSERT(qtv->isAutodetected());
316 m_versions.insert(id, qtv);
317 restored = true;
318 }
319 if (restored)
320 changed << id;
321 else
322 removed << id;
323 }
324 }
325 // Create a new qtversion
326 if (!restored) { // didn't replace any existing versions
327 qCDebug(log) << " No Qt version found matching" << autoDetectionSource << " => Creating new version";
328 if (BaseQtVersion *qtv = factory->restore(type, qtversionMap)) {
329 Q_ASSERT(qtv->isAutodetected());
330 m_versions.insert(qtv->uniqueId(), qtv);
331 added << qtv->uniqueId();
332 restored = true;
333 }
334 }
335 if (!restored) {
336 qCDebug(log, "Warning: Unable to update qtversion '%s' from sdk installer.",
337 qPrintable(autoDetectionSource));
338 }
339 }
340
341 if (log().isDebugEnabled()) {
342 qCDebug(log) << "======= Before removing outdated sdk versions =======";
343 for (BaseQtVersion *version : qAsConst(m_versions)) {
344 qCDebug(log) << version->qmakeFilePath().toUserOutput() << "id:" << version->uniqueId();
345 qCDebug(log) << " autodetection source:" << version->detectionSource();
346 qCDebug(log) << "";
347 }
348 }
349 const VersionMap versionsCopy = m_versions; // m_versions is modified in loop
350 for (BaseQtVersion *qtVersion : versionsCopy) {
351 if (qtVersion->detectionSource().startsWith("SDK.")) {
352 if (!sdkVersions.contains(qtVersion->detectionSource())) {
353 qCDebug(log) << " removing version" << qtVersion->detectionSource();
354 m_versions.remove(qtVersion->uniqueId());
355 removed << qtVersion->uniqueId();
356 }
357 }
358 }
359
360 if (log().isDebugEnabled()) {
361 qCDebug(log)<< "======= End result =======";
362 for (BaseQtVersion *version : qAsConst(m_versions)) {
363 qCDebug(log) << version->qmakeFilePath().toUserOutput() << "id:" << version->uniqueId();
364 qCDebug(log) << " autodetection source:" << version->detectionSource();
365 qCDebug(log) << "";
366 }
367 }
368 if (emitSignal)
369 emit qtVersionsChanged(added, removed, changed);
370 }
371
saveQtVersions()372 static void saveQtVersions()
373 {
374 if (!m_writer)
375 return;
376
377 QVariantMap data;
378 data.insert(QTVERSION_FILE_VERSION_KEY, 1);
379
380 int count = 0;
381 for (BaseQtVersion *qtv : qAsConst(m_versions)) {
382 QVariantMap tmp = qtv->toMap();
383 if (tmp.isEmpty())
384 continue;
385 tmp.insert(QTVERSION_TYPE_KEY, qtv->type());
386 data.insert(QString::fromLatin1(QTVERSION_DATA_KEY) + QString::number(count), tmp);
387 ++count;
388 }
389 m_writer->save(data, Core::ICore::dialogParent());
390 }
391
392 // Executes qtchooser with arguments in a process and returns its output
runQtChooser(const QString & qtchooser,const QStringList & arguments)393 static QList<QByteArray> runQtChooser(const QString &qtchooser, const QStringList &arguments)
394 {
395 QProcess p;
396 p.start(qtchooser, arguments);
397 p.waitForFinished();
398 const bool success = p.exitCode() == 0;
399 return success ? p.readAllStandardOutput().split('\n') : QList<QByteArray>();
400 }
401
402 // Asks qtchooser for the qmake path of a given version
qmakePath(const QString & qtchooser,const QString & version)403 static QString qmakePath(const QString &qtchooser, const QString &version)
404 {
405 const QList<QByteArray> outputs = runQtChooser(qtchooser,
406 {QStringLiteral("-qt=%1").arg(version),
407 QStringLiteral("-print-env")});
408 for (const QByteArray &output : outputs) {
409 if (output.startsWith("QTTOOLDIR=\"")) {
410 QByteArray withoutVarName = output.mid(11); // remove QTTOOLDIR="
411 withoutVarName.chop(1); // remove trailing quote
412 return QStandardPaths::findExecutable(QStringLiteral("qmake"), QStringList()
413 << QString::fromLocal8Bit(withoutVarName));
414 }
415 }
416 return QString();
417 }
418
gatherQmakePathsFromQtChooser()419 static FilePaths gatherQmakePathsFromQtChooser()
420 {
421 const QString qtchooser = QStandardPaths::findExecutable(QStringLiteral("qtchooser"));
422 if (qtchooser.isEmpty())
423 return FilePaths();
424
425 const QList<QByteArray> versions = runQtChooser(qtchooser, QStringList("-l"));
426 QSet<FilePath> foundQMakes;
427 for (const QByteArray &version : versions) {
428 FilePath possibleQMake = FilePath::fromString(
429 qmakePath(qtchooser, QString::fromLocal8Bit(version)));
430 if (!possibleQMake.isEmpty())
431 foundQMakes << possibleQMake;
432 }
433 return Utils::toList(foundQMakes);
434 }
435
findSystemQt()436 static void findSystemQt()
437 {
438 FilePaths systemQMakes
439 = BuildableHelperLibrary::findQtsInEnvironment(Environment::systemEnvironment());
440 systemQMakes.append(gatherQmakePathsFromQtChooser());
441 for (const FilePath &qmakePath : qAsConst(systemQMakes)) {
442 if (BuildableHelperLibrary::isQtChooser(qmakePath))
443 continue;
444 const auto isSameQmake = [qmakePath](const BaseQtVersion *version) {
445 return Environment::systemEnvironment().
446 isSameExecutable(qmakePath.toString(), version->qmakeFilePath().toString());
447 };
448 if (contains(m_versions, isSameQmake))
449 continue;
450 BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qmakePath,
451 false,
452 "PATH");
453 if (version)
454 m_versions.insert(version->uniqueId(), version);
455 }
456 }
457
addVersion(BaseQtVersion * version)458 void QtVersionManager::addVersion(BaseQtVersion *version)
459 {
460 QTC_ASSERT(m_writer, return);
461 QTC_ASSERT(version, return);
462 if (m_versions.contains(version->uniqueId()))
463 return;
464
465 int uniqueId = version->uniqueId();
466 m_versions.insert(uniqueId, version);
467
468 emit m_instance->qtVersionsChanged(QList<int>() << uniqueId, QList<int>(), QList<int>());
469 saveQtVersions();
470 }
471
removeVersion(BaseQtVersion * version)472 void QtVersionManager::removeVersion(BaseQtVersion *version)
473 {
474 QTC_ASSERT(version, return);
475 m_versions.remove(version->uniqueId());
476 emit m_instance->qtVersionsChanged(QList<int>(), QList<int>() << version->uniqueId(), QList<int>());
477 saveQtVersions();
478 delete version;
479 }
480
registerExampleSet(const QString & displayName,const QString & manifestPath,const QString & examplesPath)481 void QtVersionManager::registerExampleSet(const QString &displayName,
482 const QString &manifestPath,
483 const QString &examplesPath)
484 {
485 m_pluginRegisteredExampleSets.append({displayName, manifestPath, examplesPath});
486 }
487
488 using Path = QString;
489 using FileName = QString;
documentationFiles(BaseQtVersion * v)490 static QList<std::pair<Path, FileName>> documentationFiles(BaseQtVersion *v)
491 {
492 QList<std::pair<Path, FileName>> files;
493 const QStringList docPaths = QStringList(
494 {v->docsPath().toString() + QChar('/'), v->docsPath().toString() + "/qch/"});
495 for (const QString &docPath : docPaths) {
496 const QDir versionHelpDir(docPath);
497 for (const QString &helpFile : versionHelpDir.entryList(QStringList("*.qch"), QDir::Files))
498 files.append({docPath, helpFile});
499 }
500 return files;
501 }
502
documentationFiles(const QList<BaseQtVersion * > & vs,bool highestOnly=false)503 static QStringList documentationFiles(const QList<BaseQtVersion *> &vs, bool highestOnly = false)
504 {
505 // if highestOnly is true, register each file only once per major Qt version, even if
506 // multiple minor or patch releases of that major version are installed
507 QHash<int, QSet<QString>> includedFileNames; // major Qt version -> names
508 QSet<QString> filePaths;
509 const QList<BaseQtVersion *> versions = highestOnly ? QtVersionManager::sortVersions(vs) : vs;
510 for (BaseQtVersion *v : versions) {
511 const int majorVersion = v->qtVersion().majorVersion;
512 QSet<QString> &majorVersionFileNames = includedFileNames[majorVersion];
513 for (const std::pair<Path, FileName> &file : documentationFiles(v)) {
514 if (!highestOnly || !majorVersionFileNames.contains(file.second)) {
515 filePaths.insert(file.first + file.second);
516 majorVersionFileNames.insert(file.second);
517 }
518 }
519 }
520 return filePaths.values();
521 }
522
updateDocumentation(const QList<BaseQtVersion * > & added,const QList<BaseQtVersion * > & removed,const QList<BaseQtVersion * > & allNew)523 void QtVersionManager::updateDocumentation(const QList<BaseQtVersion *> &added,
524 const QList<BaseQtVersion *> &removed,
525 const QList<BaseQtVersion *> &allNew)
526 {
527 const DocumentationSetting setting = documentationSetting();
528 const QStringList docsOfAll = setting == DocumentationSetting::None
529 ? QStringList()
530 : documentationFiles(allNew,
531 setting
532 == DocumentationSetting::HighestOnly);
533 const QStringList docsToRemove = Utils::filtered(documentationFiles(removed),
534 [&docsOfAll](const QString &f) {
535 return !docsOfAll.contains(f);
536 });
537 const QStringList docsToAdd = Utils::filtered(documentationFiles(added),
538 [&docsOfAll](const QString &f) {
539 return docsOfAll.contains(f);
540 });
541 Core::HelpManager::unregisterDocumentation(docsToRemove);
542 Core::HelpManager::registerDocumentation(docsToAdd);
543 }
544
getUniqueId()545 int QtVersionManager::getUniqueId()
546 {
547 return m_idcount++;
548 }
549
versions(const BaseQtVersion::Predicate & predicate)550 QList<BaseQtVersion *> QtVersionManager::versions(const BaseQtVersion::Predicate &predicate)
551 {
552 QList<BaseQtVersion *> versions;
553 QTC_ASSERT(isLoaded(), return versions);
554 if (predicate)
555 return Utils::filtered(m_versions.values(), predicate);
556 return m_versions.values();
557 }
558
sortVersions(const QList<BaseQtVersion * > & input)559 QList<BaseQtVersion *> QtVersionManager::sortVersions(const QList<BaseQtVersion *> &input)
560 {
561 QList<BaseQtVersion *> result = input;
562 Utils::sort(result, qtVersionNumberCompare);
563 return result;
564 }
565
version(int id)566 BaseQtVersion *QtVersionManager::version(int id)
567 {
568 QTC_ASSERT(isLoaded(), return nullptr);
569 VersionMap::const_iterator it = m_versions.constFind(id);
570 if (it == m_versions.constEnd())
571 return nullptr;
572 return it.value();
573 }
574
version(const BaseQtVersion::Predicate & predicate)575 BaseQtVersion *QtVersionManager::version(const BaseQtVersion::Predicate &predicate)
576 {
577 return Utils::findOrDefault(m_versions.values(), predicate);
578 }
579
580 // This function is really simplistic...
equals(BaseQtVersion * a,BaseQtVersion * b)581 static bool equals(BaseQtVersion *a, BaseQtVersion *b)
582 {
583 return a->equals(b);
584 }
585
setNewQtVersions(const QList<BaseQtVersion * > & newVersions)586 void QtVersionManager::setNewQtVersions(const QList<BaseQtVersion *> &newVersions)
587 {
588 // We want to preserve the same order as in the settings dialog
589 // so we sort a copy
590 QList<BaseQtVersion *> sortedNewVersions = newVersions;
591 Utils::sort(sortedNewVersions, &BaseQtVersion::uniqueId);
592
593 QList<BaseQtVersion *> addedVersions;
594 QList<BaseQtVersion *> removedVersions;
595 QList<std::pair<BaseQtVersion *, BaseQtVersion *>> changedVersions;
596 // So we trying to find the minimal set of changed versions,
597 // iterate over both sorted list
598
599 // newVersions and oldVersions iterator
600 QList<BaseQtVersion *>::const_iterator nit, nend;
601 VersionMap::const_iterator oit, oend;
602 nit = sortedNewVersions.constBegin();
603 nend = sortedNewVersions.constEnd();
604 oit = m_versions.constBegin();
605 oend = m_versions.constEnd();
606
607 while (nit != nend && oit != oend) {
608 int nid = (*nit)->uniqueId();
609 int oid = (*oit)->uniqueId();
610 if (nid < oid) {
611 addedVersions.push_back(*nit);
612 ++nit;
613 } else if (oid < nid) {
614 removedVersions.push_back(*oit);
615 ++oit;
616 } else {
617 if (!equals(*oit, *nit))
618 changedVersions.push_back({*oit, *nit});
619 ++oit;
620 ++nit;
621 }
622 }
623
624 while (nit != nend) {
625 addedVersions.push_back(*nit);
626 ++nit;
627 }
628
629 while (oit != oend) {
630 removedVersions.push_back(*oit);
631 ++oit;
632 }
633
634 if (!changedVersions.isEmpty() || !addedVersions.isEmpty() || !removedVersions.isEmpty()) {
635 const QList<BaseQtVersion *> changedOldVersions
636 = Utils::transform(changedVersions, &std::pair<BaseQtVersion *, BaseQtVersion *>::first);
637 const QList<BaseQtVersion *> changedNewVersions
638 = Utils::transform(changedVersions,
639 &std::pair<BaseQtVersion *, BaseQtVersion *>::second);
640 updateDocumentation(addedVersions + changedNewVersions,
641 removedVersions + changedOldVersions,
642 sortedNewVersions);
643 }
644 const QList<int> addedIds = Utils::transform(addedVersions, &BaseQtVersion::uniqueId);
645 const QList<int> removedIds = Utils::transform(removedVersions, &BaseQtVersion::uniqueId);
646 const QList<int> changedIds = Utils::transform(changedVersions,
647 [](std::pair<BaseQtVersion *, BaseQtVersion *> v) {
648 return v.first->uniqueId();
649 });
650
651 qDeleteAll(m_versions);
652 m_versions = Utils::transform<VersionMap>(sortedNewVersions, [](BaseQtVersion *v) {
653 return std::make_pair(v->uniqueId(), v);
654 });
655 saveQtVersions();
656
657 if (!changedVersions.isEmpty() || !addedVersions.isEmpty() || !removedVersions.isEmpty())
658 emit m_instance->qtVersionsChanged(addedIds, removedIds, changedIds);
659 }
660
setDocumentationSetting(const QtVersionManager::DocumentationSetting & setting)661 void QtVersionManager::setDocumentationSetting(const QtVersionManager::DocumentationSetting &setting)
662 {
663 if (setting == documentationSetting())
664 return;
665 Core::ICore::settings()->setValueWithDefault(DOCUMENTATION_SETTING_KEY, int(setting), 0);
666 // force re-evaluating which documentation should be registered
667 // by claiming that all are removed and re-added
668 const QList<BaseQtVersion *> vs = versions();
669 updateDocumentation(vs, vs, vs);
670 }
671
documentationSetting()672 QtVersionManager::DocumentationSetting QtVersionManager::documentationSetting()
673 {
674 return DocumentationSetting(
675 Core::ICore::settings()->value(DOCUMENTATION_SETTING_KEY, 0).toInt());
676 }
677
678 } // namespace QtVersion
679