aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs')
-rw-r--r--src/libs/3rdparty/cplusplus/Lexer.cpp12
-rw-r--r--src/libs/3rdparty/cplusplus/Token.h1
-rw-r--r--src/libs/cplusplus/MatchingText.cpp1
-rw-r--r--src/libs/extensionsystem/pluginmanager.cpp7
-rw-r--r--src/libs/languageserverprotocol/clientcapabilities.cpp35
-rw-r--r--src/libs/languageserverprotocol/clientcapabilities.h20
-rw-r--r--src/libs/languageserverprotocol/jsonkeys.h5
-rw-r--r--src/libs/languageserverprotocol/languagefeatures.cpp56
-rw-r--r--src/libs/languageserverprotocol/languagefeatures.h56
-rw-r--r--src/libs/languageserverprotocol/servercapabilities.cpp43
-rw-r--r--src/libs/languageserverprotocol/servercapabilities.h17
-rw-r--r--src/libs/qmleditorwidgets/contextpanetextwidget.cpp2
-rw-r--r--src/libs/tracing/qml/ButtonsBar.qml30
-rw-r--r--src/libs/tracing/qml/CategoryLabel.qml16
-rw-r--r--src/libs/tracing/qml/FlameGraphView.qml28
-rw-r--r--src/libs/tracing/qml/ImageToolButton.qml20
-rw-r--r--src/libs/tracing/qml/MainView.qml28
-rw-r--r--src/libs/tracing/qml/RangeDetails.qml5
-rw-r--r--src/libs/tracing/qml/RowLabel.qml36
-rw-r--r--src/libs/tracing/qml/SelectionRangeDetails.qml3
-rw-r--r--src/libs/tracing/qml/TimelineContent.qml240
-rw-r--r--src/libs/utils/CMakeLists.txt18
-rw-r--r--src/libs/utils/algorithm.h16
-rw-r--r--src/libs/utils/buildablehelperlibrary.cpp4
-rw-r--r--src/libs/utils/consoleprocess.h12
-rw-r--r--src/libs/utils/consoleprocess_p.h2
-rw-r--r--src/libs/utils/consoleprocess_unix.cpp25
-rw-r--r--src/libs/utils/consoleprocess_win.cpp19
-rw-r--r--src/libs/utils/detailsbutton.cpp58
-rw-r--r--src/libs/utils/detailsbutton.h18
-rw-r--r--src/libs/utils/environment.cpp374
-rw-r--r--src/libs/utils/environment.h78
-rw-r--r--src/libs/utils/environmentdialog.cpp149
-rw-r--r--src/libs/utils/environmentdialog.h30
-rw-r--r--src/libs/utils/environmentfwd.h51
-rw-r--r--src/libs/utils/environmentmodel.cpp354
-rw-r--r--src/libs/utils/environmentmodel.h44
-rw-r--r--src/libs/utils/fileutils.cpp24
-rw-r--r--src/libs/utils/fileutils.h18
-rw-r--r--src/libs/utils/listmodel.h6
-rw-r--r--src/libs/utils/namevaluedictionary.cpp282
-rw-r--r--src/libs/utils/namevaluedictionary.h92
-rw-r--r--src/libs/utils/namevalueitem.cpp199
-rw-r--r--src/libs/utils/namevalueitem.h80
-rw-r--r--src/libs/utils/namevaluemodel.cpp397
-rw-r--r--src/libs/utils/namevaluemodel.h83
-rw-r--r--src/libs/utils/namevaluesdialog.cpp147
-rw-r--r--src/libs/utils/namevaluesdialog.h86
-rw-r--r--src/libs/utils/namevaluevalidator.cpp72
-rw-r--r--src/libs/utils/namevaluevalidator.h58
-rw-r--r--src/libs/utils/pathchooser.cpp13
-rw-r--r--src/libs/utils/settingsaccessor.cpp5
-rw-r--r--src/libs/utils/shellcommand.cpp53
-rw-r--r--src/libs/utils/shellcommand.h21
-rw-r--r--src/libs/utils/stringutils.cpp8
-rw-r--r--src/libs/utils/synchronousprocess.cpp27
-rw-r--r--src/libs/utils/synchronousprocess.h9
-rw-r--r--src/libs/utils/utils-lib.pri16
-rw-r--r--src/libs/utils/utils.qbs14
59 files changed, 2302 insertions, 1321 deletions
diff --git a/src/libs/3rdparty/cplusplus/Lexer.cpp b/src/libs/3rdparty/cplusplus/Lexer.cpp
index 079ae0ca6e..d1858f007c 100644
--- a/src/libs/3rdparty/cplusplus/Lexer.cpp
+++ b/src/libs/3rdparty/cplusplus/Lexer.cpp
@@ -954,7 +954,8 @@ void Lexer::scanNumericLiteral(Token *tok)
yyinp();
while (std::isdigit(_yychar) ||
(_yychar >= 'a' && _yychar <= 'f') ||
- (_yychar >= 'A' && _yychar <= 'F')) {
+ (_yychar >= 'A' && _yychar <= 'F') ||
+ ((_yychar == '\'') && _languageFeatures.cxx14Enabled)) {
yyinp();
}
if (!scanOptionalIntegerSuffix())
@@ -962,7 +963,8 @@ void Lexer::scanNumericLiteral(Token *tok)
goto theEnd;
} else if (_yychar == 'b' || _yychar == 'B') { // see n3472
yyinp();
- while (_yychar == '0' || _yychar == '1')
+ while (_yychar == '0' || _yychar == '1' ||
+ ((_yychar == '\'') && _languageFeatures.cxx14Enabled))
yyinp();
if (!scanOptionalIntegerSuffix())
scanOptionalUserDefinedLiteral(tok);
@@ -970,7 +972,8 @@ void Lexer::scanNumericLiteral(Token *tok)
} else if (_yychar >= '0' && _yychar <= '7') {
do {
yyinp();
- } while (_yychar >= '0' && _yychar <= '7');
+ } while ((_yychar >= '0' && _yychar <= '7') ||
+ ((_yychar == '\'') && _languageFeatures.cxx14Enabled));
if (!scanOptionalIntegerSuffix())
scanOptionalUserDefinedLiteral(tok);
goto theEnd;
@@ -989,7 +992,8 @@ void Lexer::scanNumericLiteral(Token *tok)
if (scanExponentPart() && !scanOptionalFloatingSuffix())
scanOptionalUserDefinedLiteral(tok);
break;
- } else if (std::isdigit(_yychar)) {
+ } else if (std::isdigit(_yychar) ||
+ ((_yychar == '\'') && _languageFeatures.cxx14Enabled)) {
yyinp();
} else {
if (!scanOptionalIntegerSuffix())
diff --git a/src/libs/3rdparty/cplusplus/Token.h b/src/libs/3rdparty/cplusplus/Token.h
index 67378b28a3..36a893efff 100644
--- a/src/libs/3rdparty/cplusplus/Token.h
+++ b/src/libs/3rdparty/cplusplus/Token.h
@@ -437,6 +437,7 @@ struct LanguageFeatures
unsigned int qtKeywordsEnabled : 1; // If Qt is used but QT_NO_KEYWORDS defined
unsigned int cxxEnabled : 1;
unsigned int cxx11Enabled : 1;
+ unsigned int cxx14Enabled : 1;
unsigned int objCEnabled : 1;
unsigned int c99Enabled : 1;
};
diff --git a/src/libs/cplusplus/MatchingText.cpp b/src/libs/cplusplus/MatchingText.cpp
index 59440813a7..5aea683f1e 100644
--- a/src/libs/cplusplus/MatchingText.cpp
+++ b/src/libs/cplusplus/MatchingText.cpp
@@ -145,6 +145,7 @@ static LanguageFeatures languageFeatures()
features.qtKeywordsEnabled = false;
features.qtMocRunEnabled = false;
features.cxx11Enabled = true;
+ features.cxx14Enabled = true;
features.cxxEnabled = true;
features.c99Enabled = true;
features.objCEnabled = true;
diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp
index 6fbda8e4ec..9ef7d8cdc7 100644
--- a/src/libs/extensionsystem/pluginmanager.cpp
+++ b/src/libs/extensionsystem/pluginmanager.cpp
@@ -49,6 +49,7 @@
#include <utils/algorithm.h>
#include <utils/benchmarker.h>
#include <utils/executeondestruction.h>
+#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/mimetypes/mimedatabase.h>
#include <utils/qtcassert.h>
@@ -413,10 +414,10 @@ static QString filled(const QString &s, int min)
QString PluginManager::systemInformation() const
{
QString result;
- const QString qtdiagBinary = HostOsInfo::withExecutableSuffix(
- QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qtdiag");
+ CommandLine qtDiag(FilePath::fromString(HostOsInfo::withExecutableSuffix(
+ QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qtdiag")));
SynchronousProcess qtdiagProc;
- const SynchronousProcessResponse response = qtdiagProc.runBlocking(qtdiagBinary, QStringList());
+ const SynchronousProcessResponse response = qtdiagProc.runBlocking(qtDiag);
if (response.result == SynchronousProcessResponse::Finished)
result += response.allOutput() + "\n";
result += "Plugin information:\n\n";
diff --git a/src/libs/languageserverprotocol/clientcapabilities.cpp b/src/libs/languageserverprotocol/clientcapabilities.cpp
index f73c4ee04c..1f79ffae0d 100644
--- a/src/libs/languageserverprotocol/clientcapabilities.cpp
+++ b/src/libs/languageserverprotocol/clientcapabilities.cpp
@@ -76,23 +76,24 @@ bool TextDocumentClientCapabilities::SynchronizationCapabilities::isValid(QStrin
bool TextDocumentClientCapabilities::isValid(QStringList *error) const
{
return checkOptional<SynchronizationCapabilities>(error, synchronizationKey)
- && checkOptional<CompletionCapabilities>(error, completionKey)
- && checkOptional<HoverCapabilities>(error, hoverKey)
- && checkOptional<SignatureHelpCapabilities>(error, signatureHelpKey)
- && checkOptional<DynamicRegistrationCapabilities>(error, referencesKey)
- && checkOptional<DynamicRegistrationCapabilities>(error, documentHighlightKey)
- && checkOptional<SymbolCapabilities>(error, documentSymbolKey)
- && checkOptional<DynamicRegistrationCapabilities>(error, formattingKey)
- && checkOptional<DynamicRegistrationCapabilities>(error, rangeFormattingKey)
- && checkOptional<DynamicRegistrationCapabilities>(error, onTypeFormattingKey)
- && checkOptional<DynamicRegistrationCapabilities>(error, definitionKey)
- && checkOptional<DynamicRegistrationCapabilities>(error, typeDefinitionKey)
- && checkOptional<DynamicRegistrationCapabilities>(error, implementationKey)
- && checkOptional<CodeActionCapabilities>(error, codeActionKey)
- && checkOptional<DynamicRegistrationCapabilities>(error, codeLensKey)
- && checkOptional<DynamicRegistrationCapabilities>(error, documentLinkKey)
- && checkOptional<DynamicRegistrationCapabilities>(error, colorProviderKey)
- && checkOptional<DynamicRegistrationCapabilities>(error, renameKey);
+ && checkOptional<CompletionCapabilities>(error, completionKey)
+ && checkOptional<HoverCapabilities>(error, hoverKey)
+ && checkOptional<SignatureHelpCapabilities>(error, signatureHelpKey)
+ && checkOptional<DynamicRegistrationCapabilities>(error, referencesKey)
+ && checkOptional<DynamicRegistrationCapabilities>(error, documentHighlightKey)
+ && checkOptional<SymbolCapabilities>(error, documentSymbolKey)
+ && checkOptional<DynamicRegistrationCapabilities>(error, formattingKey)
+ && checkOptional<DynamicRegistrationCapabilities>(error, rangeFormattingKey)
+ && checkOptional<DynamicRegistrationCapabilities>(error, onTypeFormattingKey)
+ && checkOptional<DynamicRegistrationCapabilities>(error, definitionKey)
+ && checkOptional<DynamicRegistrationCapabilities>(error, typeDefinitionKey)
+ && checkOptional<DynamicRegistrationCapabilities>(error, implementationKey)
+ && checkOptional<CodeActionCapabilities>(error, codeActionKey)
+ && checkOptional<DynamicRegistrationCapabilities>(error, codeLensKey)
+ && checkOptional<DynamicRegistrationCapabilities>(error, documentLinkKey)
+ && checkOptional<DynamicRegistrationCapabilities>(error, colorProviderKey)
+ && checkOptional<DynamicRegistrationCapabilities>(error, renameKey)
+ && checkOptional<SemanticHighlightingCapabilities>(error, semanticHighlightingCapabilitiesKey);
}
bool SymbolCapabilities::isValid(QStringList *error) const
diff --git a/src/libs/languageserverprotocol/clientcapabilities.h b/src/libs/languageserverprotocol/clientcapabilities.h
index 3664007dd1..ec1bdd25b5 100644
--- a/src/libs/languageserverprotocol/clientcapabilities.h
+++ b/src/libs/languageserverprotocol/clientcapabilities.h
@@ -120,6 +120,26 @@ public:
{ insert(synchronizationKey, synchronization); }
void clearSynchronization() { remove(synchronizationKey); }
+ class LANGUAGESERVERPROTOCOL_EXPORT SemanticHighlightingCapabilities : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ bool semanticHighlighting() const { return typedValue<bool>(semanticHighlightingKey); }
+ void setSemanticHighlighting(bool semanticHighlighting)
+ { insert(semanticHighlightingKey, semanticHighlighting); }
+
+ bool isValid(QStringList *error) const override
+ { return check<bool>(error, semanticHighlightingKey); }
+ };
+
+ Utils::optional<SemanticHighlightingCapabilities> semanticHighlightingCapabilities() const
+ { return optionalValue<SemanticHighlightingCapabilities>(semanticHighlightingCapabilitiesKey); }
+ void setSemanticHighlightingCapabilities(
+ const SemanticHighlightingCapabilities &semanticHighlightingCapabilities)
+ { insert(semanticHighlightingCapabilitiesKey, semanticHighlightingCapabilities); }
+ void clearSemanticHighlightingCapabilities() { remove(semanticHighlightingCapabilitiesKey); }
+
class LANGUAGESERVERPROTOCOL_EXPORT CompletionCapabilities : public DynamicRegistrationCapabilities
{
public:
diff --git a/src/libs/languageserverprotocol/jsonkeys.h b/src/libs/languageserverprotocol/jsonkeys.h
index e3673085eb..4c4ee054ab 100644
--- a/src/libs/languageserverprotocol/jsonkeys.h
+++ b/src/libs/languageserverprotocol/jsonkeys.h
@@ -130,6 +130,7 @@ constexpr char labelKey[] = "label";
constexpr char languageIdKey[] = "languageId";
constexpr char languageKey[] = "language";
constexpr char lineKey[] = "line";
+constexpr char linesKey[] = "lines";
constexpr char locationKey[] = "location";
constexpr char messageKey[] = "message";
constexpr char methodKey[] = "method";
@@ -166,8 +167,11 @@ constexpr char rootUriKey[] = "rootUri";
constexpr char saveKey[] = "save";
constexpr char schemeKey[] = "scheme";
constexpr char scopeUriKey[] = "scopeUri";
+constexpr char scopesKey[] = "scopes";
constexpr char sectionKey[] = "section";
constexpr char selectionRangeKey[] = "selectionRange";
+constexpr char semanticHighlightingKey[] = "semanticHighlighting";
+constexpr char semanticHighlightingCapabilitiesKey[] = "semanticHighlightingCapabilities";
constexpr char settingsKey[] = "settings";
constexpr char severityKey[] = "severity";
constexpr char signatureHelpKey[] = "signatureHelp";
@@ -190,6 +194,7 @@ constexpr char textDocumentSyncKey[] = "textDocumentSync";
constexpr char textEditKey[] = "textEdit";
constexpr char textKey[] = "text";
constexpr char titleKey[] = "title";
+constexpr char tokensKey[] = "tokens";
constexpr char traceKey[] = "trace";
constexpr char triggerCharacterKey[] = "triggerCharacter";
constexpr char triggerCharactersKey[] = "triggerCharacters";
diff --git a/src/libs/languageserverprotocol/languagefeatures.cpp b/src/libs/languageserverprotocol/languagefeatures.cpp
index cfb4cc6fab..f66b6b7e16 100644
--- a/src/libs/languageserverprotocol/languagefeatures.cpp
+++ b/src/libs/languageserverprotocol/languagefeatures.cpp
@@ -48,6 +48,7 @@ constexpr const char DocumentRangeFormattingRequest::methodName[];
constexpr const char DocumentOnTypeFormattingRequest::methodName[];
constexpr const char RenameRequest::methodName[];
constexpr const char SignatureHelpRequest::methodName[];
+constexpr const char SemanticHighlightNotification::methodName[];
HoverContent LanguageServerProtocol::Hover::content() const
{
@@ -441,4 +442,59 @@ bool CodeAction::isValid(QStringList *error) const
&& checkOptional<Command>(error, commandKey);
}
+Utils::optional<QList<SemanticHighlightToken>> SemanticHighlightingInformation::tokens() const
+{
+ QList<SemanticHighlightToken> resultTokens;
+
+ const QByteArray tokensByteArray = QByteArray::fromBase64(
+ typedValue<QString>(tokensKey).toLocal8Bit());
+ constexpr int tokensByteSize = 8;
+ int index = 0;
+ while (index + tokensByteSize <= tokensByteArray.size()) {
+ resultTokens << SemanticHighlightToken(tokensByteArray.mid(index, tokensByteSize));
+ index += tokensByteSize;
+ }
+ return Utils::make_optional(resultTokens);
+}
+
+void SemanticHighlightingInformation::setTokens(const QList<SemanticHighlightToken> &tokens)
+{
+ QByteArray byteArray;
+ byteArray.reserve(8 * tokens.size());
+ for (const SemanticHighlightToken &token : tokens)
+ token.appendToByteArray(byteArray);
+ insert(tokensKey, QString::fromLocal8Bit(byteArray.toBase64()));
+}
+
+SemanticHighlightToken::SemanticHighlightToken(const QByteArray &token)
+{
+ QTC_ASSERT(token.size() == 8, return );
+ character = ( quint32(token.at(0)) << 24
+ | quint32(token.at(1)) << 16
+ | quint32(token.at(2)) << 8
+ | quint32(token.at(3)));
+
+ length = quint16(token.at(4) << 8 | token.at(5));
+
+ scope = quint16(token.at(6) << 8 | token.at(7));
+}
+
+void SemanticHighlightToken::appendToByteArray(QByteArray &byteArray) const
+{
+ byteArray.append(char((character & 0xff000000) >> 24));
+ byteArray.append(char((character & 0x00ff0000) >> 16));
+ byteArray.append(char((character & 0x0000ff00) >> 8));
+ byteArray.append(char((character & 0x000000ff)));
+ byteArray.append(char((length & 0xff00) >> 8));
+ byteArray.append(char((length & 0x00ff)));
+ byteArray.append(char((scope & 0xff00) >> 8));
+ byteArray.append(char((scope & 0x00ff)));
+}
+
+bool SemanticHighlightingParams::isValid(QStringList *error) const
+{
+ return check<VersionedTextDocumentIdentifier>(error, textDocumentKey)
+ && checkArray<SemanticHighlightingInformation>(error, linesKey);
+}
+
} // namespace LanguageServerProtocol
diff --git a/src/libs/languageserverprotocol/languagefeatures.h b/src/libs/languageserverprotocol/languagefeatures.h
index ddd732718a..bb98d7f8bd 100644
--- a/src/libs/languageserverprotocol/languagefeatures.h
+++ b/src/libs/languageserverprotocol/languagefeatures.h
@@ -806,4 +806,60 @@ public:
constexpr static const char methodName[] = "textDocument/rename";
};
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticHighlightToken
+{
+public:
+ // Just accepts token with 8 bytes
+ SemanticHighlightToken(const QByteArray &token);
+ SemanticHighlightToken() = default;
+
+ void appendToByteArray(QByteArray &byteArray) const;
+
+ quint32 character = 0;
+ quint16 length = 0;
+ quint16 scope = 0;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticHighlightingInformation : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ int line() const { return typedValue<int>(lineKey); }
+ void setLine(int line) { insert(lineKey, line); }
+
+ Utils::optional<QList<SemanticHighlightToken>> tokens() const;
+ void setTokens(const QList<SemanticHighlightToken> &tokens);
+ void clearTokens() { remove(tokensKey); }
+
+ bool isValid(QStringList *error) const override
+ { return check<int>(error, lineKey) && checkOptional<QString>(error, tokensKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticHighlightingParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ VersionedTextDocumentIdentifier textDocument() const
+ { return typedValue<VersionedTextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(const VersionedTextDocumentIdentifier &textDocument)
+ { insert(textDocumentKey, textDocument); }
+
+ QList<SemanticHighlightingInformation> lines() const
+ { return array<SemanticHighlightingInformation>(linesKey); }
+ void setLines(const QList<SemanticHighlightingInformation> &lines)
+ { insertArray(linesKey, lines); }
+
+ bool isValid(QStringList *error) const override;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticHighlightNotification
+ : public Notification<SemanticHighlightingParams>
+{
+public:
+ using Notification::Notification;
+ constexpr static const char methodName[] = "textDocument/semanticHighlighting";
+};
+
} // namespace LanguageClient
diff --git a/src/libs/languageserverprotocol/servercapabilities.cpp b/src/libs/languageserverprotocol/servercapabilities.cpp
index 6ebf4f431f..f9dce15b35 100644
--- a/src/libs/languageserverprotocol/servercapabilities.cpp
+++ b/src/libs/languageserverprotocol/servercapabilities.cpp
@@ -132,7 +132,8 @@ bool ServerCapabilities::isValid(QStringList *error) const
&& checkOptional<DocumentLinkOptions>(error, documentLinkProviderKey)
&& checkOptional<TextDocumentRegistrationOptions>(error, colorProviderKey)
&& checkOptional<ExecuteCommandOptions>(error, executeCommandProviderKey)
- && checkOptional<WorkspaceServerCapabilities>(error, workspaceKey);
+ && checkOptional<WorkspaceServerCapabilities>(error, workspaceKey)
+ && checkOptional<SemanticHighlightingServerCapabilities>(error, semanticHighlightingKey);
}
Utils::optional<Utils::variant<QString, bool> >
@@ -181,4 +182,44 @@ bool TextDocumentSyncOptions::isValid(QStringList *error) const
&& checkOptional<SaveOptions>(error, saveKey);
}
+Utils::optional<QList<QList<QString>>> ServerCapabilities::SemanticHighlightingServerCapabilities::scopes() const
+{
+ QList<QList<QString>> scopes;
+ if (!contains(scopesKey))
+ return Utils::nullopt;
+ for (const QJsonValue jsonScopeValue : value(scopesKey).toArray()) {
+ if (!jsonScopeValue.isArray())
+ return {};
+ QList<QString> scope;
+ for (const QJsonValue value : jsonScopeValue.toArray()) {
+ if (!value.isString())
+ return {};
+ scope.append(value.toString());
+ }
+ scopes.append(scope);
+ }
+ return Utils::make_optional(scopes);
+}
+
+void ServerCapabilities::SemanticHighlightingServerCapabilities::setScopes(
+ const QList<QList<QString>> &scopes)
+{
+ QJsonArray jsonScopes;
+ for (const QList<QString> &scope : scopes) {
+ QJsonArray jsonScope;
+ for (const QString &value : scope)
+ jsonScope.append(value);
+ jsonScopes.append(jsonScope);
+ }
+ insert(scopesKey, jsonScopes);
+}
+
+bool ServerCapabilities::SemanticHighlightingServerCapabilities::isValid(QStringList *) const
+{
+ return contains(scopesKey) && value(scopesKey).isArray()
+ && Utils::allOf(value(scopesKey).toArray(), [](const QJsonValue &array) {
+ return array.isArray() && Utils::allOf(array.toArray(), &QJsonValue::isString);
+ });
+}
+
} // namespace LanguageServerProtocol
diff --git a/src/libs/languageserverprotocol/servercapabilities.h b/src/libs/languageserverprotocol/servercapabilities.h
index 71d3e4a496..6345178c57 100644
--- a/src/libs/languageserverprotocol/servercapabilities.h
+++ b/src/libs/languageserverprotocol/servercapabilities.h
@@ -223,6 +223,17 @@ public:
void clearId() { remove(idKey); }
};
+ class LANGUAGESERVERPROTOCOL_EXPORT SemanticHighlightingServerCapabilities : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ Utils::optional<QList<QList<QString>>> scopes() const;
+ void setScopes(const QList<QList<QString>> &scopes);
+
+ bool isValid(QStringList *) const override;
+ };
+
// Defines how text documents are synced. Is either a detailed structure defining each
// notification or for backwards compatibility the TextDocumentSyncKind number.
using TextDocumentSync = Utils::variant<TextDocumentSyncOptions, int>;
@@ -411,6 +422,12 @@ public:
void setExperimental(const JsonObject &experimental) { insert(experimentalKey, experimental); }
void clearExperimental() { remove(experimentalKey); }
+ Utils::optional<SemanticHighlightingServerCapabilities> semanticHighlighting() const
+ { return optionalValue<SemanticHighlightingServerCapabilities>(semanticHighlightingKey); }
+ void setSemanticHighlighting(const SemanticHighlightingServerCapabilities &semanticHighlighting)
+ { insert(semanticHighlightingKey, semanticHighlighting); }
+ void clearSemanticHighlighting() { remove(semanticHighlightingKey); }
+
bool isValid(QStringList *error) const override;
};
diff --git a/src/libs/qmleditorwidgets/contextpanetextwidget.cpp b/src/libs/qmleditorwidgets/contextpanetextwidget.cpp
index 70b6c700db..6a37637a6a 100644
--- a/src/libs/qmleditorwidgets/contextpanetextwidget.cpp
+++ b/src/libs/qmleditorwidgets/contextpanetextwidget.cpp
@@ -103,7 +103,7 @@ ContextPaneTextWidget::ContextPaneTextWidget(QWidget *parent) :
connect(ui->bottomAlignmentButton, &QToolButton::toggled,
this, &ContextPaneTextWidget::onVerticalAlignmentChanged);
- connect(ui->styleComboBox, QOverload<const QString &>::of(&QComboBox::currentIndexChanged),
+ connect(ui->styleComboBox, &QComboBox::currentTextChanged,
this, &ContextPaneTextWidget::onStyleComboBoxChanged);
}
diff --git a/src/libs/tracing/qml/ButtonsBar.qml b/src/libs/tracing/qml/ButtonsBar.qml
index a151eb9f91..32111daa57 100644
--- a/src/libs/tracing/qml/ButtonsBar.qml
+++ b/src/libs/tracing/qml/ButtonsBar.qml
@@ -24,9 +24,8 @@
****************************************************************************/
import QtQuick 2.1
-import QtQuick.Controls 1.0
-import QtQuick.Layouts 1.0
-import QtQuick.Controls.Styles 1.2
+import QtQuick.Controls 2.0
+import QtQuick.Layouts 1.3
import TimelineTheme 1.0
@@ -55,19 +54,12 @@ ToolBar {
return rangeButton.checked
}
- style: ToolBarStyle {
- padding {
- left: 0
- right: 0
- top: 0
- bottom: 0
- }
- background: Rectangle {
- anchors.fill: parent
- color: Theme.color(Theme.PanelStatusBarBackgroundColor)
- }
+ background: Rectangle {
+ anchors.fill: parent
+ color: Theme.color(Theme.PanelStatusBarBackgroundColor)
}
+
RowLayout {
spacing: 0
anchors.fill: parent
@@ -77,7 +69,7 @@ ToolBar {
Layout.fillHeight: true
imageSource: "image://icons/prev"
- tooltip: qsTr("Jump to previous event.")
+ ToolTip.text: qsTr("Jump to previous event.")
onClicked: buttons.jumpToPrev()
}
@@ -86,7 +78,7 @@ ToolBar {
Layout.fillHeight: true
imageSource: "image://icons/next"
- tooltip: qsTr("Jump to next event.")
+ ToolTip.text: qsTr("Jump to next event.")
onClicked: buttons.jumpToNext()
}
@@ -95,7 +87,7 @@ ToolBar {
Layout.fillHeight: true
imageSource: "image://icons/zoom"
- tooltip: qsTr("Show zoom slider.")
+ ToolTip.text: qsTr("Show zoom slider.")
checkable: true
checked: false
onCheckedChanged: buttons.zoomControlChanged()
@@ -106,7 +98,7 @@ ToolBar {
Layout.fillHeight: true
imageSource: "image://icons/" + (checked ? "rangeselected" : "rangeselection");
- tooltip: qsTr("Select range.")
+ ToolTip.text: qsTr("Select range.")
checkable: true
checked: false
onCheckedChanged: buttons.rangeSelectChanged()
@@ -117,7 +109,7 @@ ToolBar {
Layout.fillHeight: true
imageSource: "image://icons/selectionmode"
- tooltip: qsTr("View event information on mouseover.")
+ ToolTip.text: qsTr("View event information on mouseover.")
checkable: true
checked: false
onCheckedChanged: buttons.lockChanged()
diff --git a/src/libs/tracing/qml/CategoryLabel.qml b/src/libs/tracing/qml/CategoryLabel.qml
index 5945b0f6df..4994d3b962 100644
--- a/src/libs/tracing/qml/CategoryLabel.qml
+++ b/src/libs/tracing/qml/CategoryLabel.qml
@@ -24,8 +24,7 @@
****************************************************************************/
import QtQuick 2.1
-import QtQuick.Controls 1.2
-import QtQuick.Controls.Styles 1.2
+import QtQuick.Controls 2.2
import TimelineTheme 1.0
@@ -54,6 +53,15 @@ Item {
property bool reverseSelect: false
+ Button {
+ // dummy button to display a tooltip
+ anchors.fill: txt
+ ToolTip.text: labelContainer.text
+ ToolTip.visible: enabled && hovered
+ ToolTip.delay: 1000
+ background: Item {}
+ }
+
MouseArea {
id: dragArea
anchors.fill: txt
@@ -161,7 +169,7 @@ Item {
visible: eventIds.length > 0
imageSource: "image://icons/note"
- tooltip: texts.join("\n");
+ ToolTip.text: texts.join("\n");
onClicked: {
if (++currentNote >= eventIds.length)
currentNote = 0;
@@ -176,7 +184,7 @@ Item {
implicitHeight: txt.height - 1
enabled: expanded || (model && !model.empty)
imageSource: expanded ? "image://icons/close_split" : "image://icons/split"
- tooltip: expanded ? qsTr("Collapse category") : qsTr("Expand category")
+ ToolTip.text: expanded ? qsTr("Collapse category") : qsTr("Expand category")
onClicked: model.expanded = !expanded
}
diff --git a/src/libs/tracing/qml/FlameGraphView.qml b/src/libs/tracing/qml/FlameGraphView.qml
index 9712b91cd5..f98bcb3134 100644
--- a/src/libs/tracing/qml/FlameGraphView.qml
+++ b/src/libs/tracing/qml/FlameGraphView.qml
@@ -28,7 +28,7 @@ import TimelineTheme 1.0
import QtQml 2.2
import QtQuick 2.9
-import QtQuick.Controls 1.3
+import QtQuick.Controls 2.3
ScrollView {
id: root
@@ -45,7 +45,7 @@ ScrollView {
function resetRoot() { flamegraph.resetRoot(); }
property bool zoomed: flamegraph.zoomed
- property int sizeRole: -1
+ property var sizeRole: modes[Math.max(0, modesMenu.currentIndex)]
property var model: null
property int typeIdRole: -1
@@ -56,7 +56,7 @@ ScrollView {
property int summaryRole: -1
property int noteRole: -1
- property var trRoleNames: []
+ property var trRoleNames: ({})
property var modes: []
@@ -314,27 +314,11 @@ ScrollView {
}
}
- Button {
+ ComboBox {
+ id: modesMenu
x: flickable.width - width
y: flickable.contentY
-
- // It won't listen to anchors.margins and by default it doesn't add any margin. Great.
- width: implicitWidth + 20
-
- text: qsTr("Visualize %1").arg(trRoleNames[root.sizeRole])
-
- menu: Menu {
- id: modesMenu
- Instantiator {
- model: root.modes
- MenuItem {
- text: root.trRoleNames[modelData]
- onTriggered: root.sizeRole = modelData
- }
- onObjectAdded: modesMenu.insertItem(index, object)
- onObjectRemoved: modesMenu.removeItem(object)
- }
- }
+ model: root.modes.map(function(role) { return root.trRoleNames[role] });
}
}
}
diff --git a/src/libs/tracing/qml/ImageToolButton.qml b/src/libs/tracing/qml/ImageToolButton.qml
index 353e38f122..686fd2db88 100644
--- a/src/libs/tracing/qml/ImageToolButton.qml
+++ b/src/libs/tracing/qml/ImageToolButton.qml
@@ -24,8 +24,7 @@
****************************************************************************/
import QtQuick 2.1
-import QtQuick.Controls 1.0
-import QtQuick.Controls.Styles 1.2
+import QtQuick.Controls 2.0
import TimelineTheme 1.0
@@ -34,6 +33,9 @@ ToolButton {
property string imageSource
+ ToolTip.visible: enabled && hovered
+ ToolTip.delay: 1000
+
Image {
source: parent.enabled ? parent.imageSource : parent.imageSource + "/disabled"
width: 16
@@ -42,13 +44,11 @@ ToolButton {
smooth: false
}
- style: ButtonStyle {
- background: Rectangle {
- color: (control.checked || control.pressed)
- ? Theme.color(Theme.FancyToolButtonSelectedColor)
- : control.hovered
- ? Theme.color(Theme.FancyToolButtonHoverColor)
- : "#00000000"
- }
+ background: Rectangle {
+ color: (parent.checked || parent.pressed)
+ ? Theme.color(Theme.FancyToolButtonSelectedColor)
+ : parent.hovered
+ ? Theme.color(Theme.FancyToolButtonHoverColor)
+ : "#00000000"
}
}
diff --git a/src/libs/tracing/qml/MainView.qml b/src/libs/tracing/qml/MainView.qml
index 2e1f97a3ba..30eb7c3c3f 100644
--- a/src/libs/tracing/qml/MainView.qml
+++ b/src/libs/tracing/qml/MainView.qml
@@ -24,8 +24,7 @@
****************************************************************************/
import QtQuick 2.1
-import QtQuick.Controls 1.0
-import QtQuick.Controls.Styles 1.0
+import QtQuick.Controls 2.0
import TimelineTheme 1.0
@@ -73,7 +72,7 @@ Rectangle {
rangeDetails.hide();
selectionRangeMode = false;
zoomSlider.externalUpdate = true;
- zoomSlider.value = zoomSlider.minimumValue;
+ zoomSlider.value = zoomSlider.from;
}
// This is called from outside to synchronize the timeline to other views
@@ -192,6 +191,7 @@ Rectangle {
TimelineContent {
id: content
+
anchors.left: buttonsBar.right
anchors.top: buttonsBar.bottom
anchors.bottom: overview.top
@@ -255,13 +255,13 @@ Rectangle {
onReleased: {
if (selectionRange.creationState === selectionRange.creationSecondLimit) {
- content.stayInteractive = true;
+ content.interactive = true;
selectionRange.creationState = selectionRange.creationFinished;
}
}
onPressed: {
if (selectionRange.creationState === selectionRange.creationFirstLimit) {
- content.stayInteractive = false;
+ content.interactive = false;
selectionRange.setPos(selectionRangeControl.mouseX + content.contentX);
selectionRange.creationState = selectionRange.creationSecondLimit;
}
@@ -281,12 +281,12 @@ Rectangle {
flickableDirection: Flickable.HorizontalFlick
clip: true
interactive: false
- x: content.x + content.flickableItem.x
- y: content.y + content.flickableItem.y
+ x: content.x
+ y: content.y
height: (selectionRangeMode &&
selectionRange.creationState !== selectionRange.creationInactive) ?
- content.flickableItem.height : 0
- width: content.flickableItem.width
+ content.height : 0
+ width: content.width
contentX: content.contentX
contentWidth: content.contentWidth
@@ -320,11 +320,11 @@ Rectangle {
}
TimelineRulers {
- contentX: buttonsBar.width - content.x - content.flickableItem.x + content.contentX
+ contentX: buttonsBar.width + content.contentX
anchors.left: buttonsBar.right
anchors.right: parent.right
anchors.top: parent.top
- height: content.flickableItem.height + buttonsBar.height
+ height: content.height + buttonsBar.height
windowStart: zoomControl.windowStart
viewTimePerPixel: selectionRange.viewTimePerPixel
scaleHeight: buttonsBar.height
@@ -458,8 +458,8 @@ Rectangle {
Slider {
id: zoomSlider
anchors.fill: parent
- minimumValue: 1
- maximumValue: 10000
+ from: 1
+ to: 10000
stepSize: 100
property int exponent: 3
@@ -478,7 +478,7 @@ Rectangle {
}
var windowLength = Math.max(
- Math.pow(value / maximumValue, exponent) * zoomControl.windowDuration,
+ Math.pow(value / to, exponent) * zoomControl.windowDuration,
minWindowLength);
var startTime = Math.max(zoomControl.windowStart, fixedPoint - windowLength / 2)
diff --git a/src/libs/tracing/qml/RangeDetails.qml b/src/libs/tracing/qml/RangeDetails.qml
index ccb86a5c3c..cb99479ca4 100644
--- a/src/libs/tracing/qml/RangeDetails.qml
+++ b/src/libs/tracing/qml/RangeDetails.qml
@@ -24,6 +24,8 @@
****************************************************************************/
import QtQuick 2.9
+import QtQuick.Controls 2.0
+
import TimelineTheme 1.0
Item {
@@ -108,6 +110,7 @@ Item {
implicitHeight: typeTitle.height
visible: !rangeDetails.noteReadonly
onClicked: noteEdit.focus = true
+ ToolTip.text: qsTr("Edit note")
}
ImageToolButton {
@@ -117,6 +120,7 @@ Item {
anchors.right: closeIcon.left
implicitHeight: typeTitle.height
onClicked: locked = !locked
+ ToolTip.text: qsTr("View event information on mouseover.")
}
ImageToolButton {
@@ -126,6 +130,7 @@ Item {
implicitHeight: typeTitle.height
imageSource: "image://icons/close_window"
onClicked: rangeDetails.clearSelection()
+ ToolTip.text: qsTr("Close")
}
}
diff --git a/src/libs/tracing/qml/RowLabel.qml b/src/libs/tracing/qml/RowLabel.qml
index be394b5e7e..e73d51b291 100644
--- a/src/libs/tracing/qml/RowLabel.qml
+++ b/src/libs/tracing/qml/RowLabel.qml
@@ -24,8 +24,7 @@
****************************************************************************/
import QtQuick 2.0
-import QtQuick.Controls 1.2
-import QtQuick.Controls.Styles 1.2
+import QtQuick.Controls 2.2
import TimelineTheme 1.0
Button {
@@ -38,25 +37,26 @@ Button {
signal setRowHeight(int newHeight)
property string labelText: label.description ? label.description : qsTr("[unknown]")
- action: Action {
- onTriggered: button.selectBySelectionId();
- tooltip: button.labelText + (label.displayName ? (" (" + label.displayName + ")") : "")
+
+ onPressed: selectBySelectionId();
+ ToolTip.text: labelText + (label.displayName ? (" (" + label.displayName + ")") : "")
+ ToolTip.visible: hovered
+ ToolTip.delay: 1000
+
+ background: Rectangle {
+ border.width: 1
+ border.color: Theme.color(Theme.Timeline_DividerColor)
+ color: Theme.color(Theme.PanelStatusBarBackgroundColor)
}
- style: ButtonStyle {
- background: Rectangle {
- border.width: 1
- border.color: Theme.color(Theme.Timeline_DividerColor)
- color: Theme.color(Theme.PanelStatusBarBackgroundColor)
- }
- label: TimelineText {
- text: button.labelText
- verticalAlignment: Text.AlignVCenter
- horizontalAlignment: Text.AlignLeft
- elide: Text.ElideRight
- color: Theme.color(Theme.PanelTextColorLight)
- }
+ contentItem: TimelineText {
+ text: button.labelText
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignLeft
+ elide: Text.ElideRight
+ color: Theme.color(Theme.PanelTextColorLight)
}
+
MouseArea {
hoverEnabled: true
property bool resizing: false
diff --git a/src/libs/tracing/qml/SelectionRangeDetails.qml b/src/libs/tracing/qml/SelectionRangeDetails.qml
index 458e5cf7ad..288ac7693c 100644
--- a/src/libs/tracing/qml/SelectionRangeDetails.qml
+++ b/src/libs/tracing/qml/SelectionRangeDetails.qml
@@ -24,6 +24,8 @@
****************************************************************************/
import QtQuick 2.1
+import QtQuick.Controls 2.0
+
import TimelineTheme 1.0
import TimelineTimeFormatter 1.0
@@ -133,5 +135,6 @@ Item {
anchors.top: selectionRangeDetails.top
implicitHeight: typeTitle.height
onClicked: selectionRangeDetails.close()
+ ToolTip.text: qsTr("Close")
}
}
diff --git a/src/libs/tracing/qml/TimelineContent.qml b/src/libs/tracing/qml/TimelineContent.qml
index 636d707356..5eb667822f 100644
--- a/src/libs/tracing/qml/TimelineContent.qml
+++ b/src/libs/tracing/qml/TimelineContent.qml
@@ -24,20 +24,26 @@
****************************************************************************/
import QtQuick 2.0
-import QtQuick.Controls 1.2
+import QtQuick.Controls 2.2
import TimelineRenderer 1.0
import QtQml.Models 2.1
-ScrollView {
- id: scroller
+Flickable {
+ id: flick
+ clip: true
+ contentHeight: timelineView.height + height
+ flickableDirection: Flickable.HorizontalAndVerticalFlick
+ boundsBehavior: Flickable.StopAtBounds
+ pixelAligned: true
+
+ ScrollBar.horizontal: ScrollBar {}
+ ScrollBar.vertical: ScrollBar {}
+
+ property bool recursionGuard: false
property QtObject zoomer
property QtObject modelProxy
- property int contentX: flick.contentX
- property int contentY: flick.contentY
- property int contentWidth: flick.contentWidth
- property int contentHeight: flick.contentHeight
property bool selectionLocked
property int typeId
@@ -58,146 +64,122 @@ ScrollView {
timelineModel.items.move(sourceIndex, targetIndex)
}
- function scroll()
- {
- flick.scroll();
+
+ function guarded(operation) {
+ if (recursionGuard)
+ return;
+ recursionGuard = true;
+ operation();
+ recursionGuard = false;
}
- // ScrollView will try to deinteractivate it. We don't want that
- // as the horizontal flickable is interactive, too. We do occasionally
- // switch to non-interactive ourselves, though.
- property bool stayInteractive: true
- onStayInteractiveChanged: flick.interactive = stayInteractive
-
- Flickable {
- id: flick
- contentHeight: timelineView.height + height
- flickableDirection: Flickable.HorizontalAndVerticalFlick
- boundsBehavior: Flickable.StopAtBounds
- pixelAligned: true
-
- onInteractiveChanged: interactive = stayInteractive
-
- property bool recursionGuard: false
-
- function guarded(operation) {
- if (recursionGuard)
- return;
- recursionGuard = true;
- operation();
- recursionGuard = false;
+ // Logically we should bind to flick.width above as we use flick.width in scroll().
+ // However, this width changes before flick.width when the window is resized and if we
+ // don't explicitly set contentX here, for some reason an automatic change in contentX is
+ // triggered after this width has changed, but before flick.width changes. This would be
+ // indistinguishabe from a manual flick by the user and thus changes the range position. We
+ // don't want to change the range position on resizing the window. Therefore we bind to this
+ // width.
+ onWidthChanged: scroll()
+
+ // Update the zoom control on scrolling.
+ onContentXChanged: guarded(function() {
+ var newStartTime = contentX * zoomer.rangeDuration / width + zoomer.windowStart;
+ if (isFinite(newStartTime) && Math.abs(newStartTime - zoomer.rangeStart) >= 1) {
+ var newEndTime = (contentX + width) * zoomer.rangeDuration / width + zoomer.windowStart;
+ if (isFinite(newEndTime))
+ zoomer.setRange(newStartTime, newEndTime);
}
-
- // Logically we should bind to scroller.width above as we use scroller.width in scroll().
- // However, this width changes before scroller.width when the window is resized and if we
- // don't explicitly set contentX here, for some reason an automatic change in contentX is
- // triggered after this width has changed, but before scroller.width changes. This would be
- // indistinguishabe from a manual flick by the user and thus changes the range position. We
- // don't want to change the range position on resizing the window. Therefore we bind to this
- // width.
- onWidthChanged: scroll()
-
- // Update the zoom control on scrolling.
- onContentXChanged: guarded(function() {
- var newStartTime = contentX * zoomer.rangeDuration / scroller.width
- + zoomer.windowStart;
- if (isFinite(newStartTime) && Math.abs(newStartTime - zoomer.rangeStart) >= 1) {
- var newEndTime = (contentX + scroller.width) * zoomer.rangeDuration / scroller.width
- + zoomer.windowStart;
- if (isFinite(newEndTime))
- zoomer.setRange(newStartTime, newEndTime);
+ });
+
+ // Scroll when the zoom control is updated
+ function scroll() {
+ guarded(function() {
+ if (zoomer.rangeDuration <= 0) {
+ contentWidth = 0;
+ contentX = 0;
+ } else {
+ var newWidth = zoomer.windowDuration * width / zoomer.rangeDuration;
+ if (isFinite(newWidth) && Math.abs(newWidth - contentWidth) >= 1)
+ contentWidth = newWidth;
+ var newStartX = (zoomer.rangeStart - zoomer.windowStart) * width /
+ zoomer.rangeDuration;
+ if (isFinite(newStartX) && Math.abs(newStartX - contentX) >= 1)
+ contentX = newStartX;
}
});
+ }
- // Scroll when the zoom control is updated
- function scroll() {
- guarded(function() {
- if (zoomer.rangeDuration <= 0) {
- contentWidth = 0;
- contentX = 0;
- } else {
- var newWidth = zoomer.windowDuration * scroller.width / zoomer.rangeDuration;
- if (isFinite(newWidth) && Math.abs(newWidth - contentWidth) >= 1)
- contentWidth = newWidth;
- var newStartX = (zoomer.rangeStart - zoomer.windowStart) * scroller.width /
- zoomer.rangeDuration;
- if (isFinite(newStartX) && Math.abs(newStartX - contentX) >= 1)
- contentX = newStartX;
- }
- });
- }
-
- Column {
- id: timelineView
-
- signal clearChildren
- signal select(int modelIndex, int eventIndex)
-
- DelegateModel {
- id: timelineModel
- model: modelProxy.models
- delegate: TimelineRenderer {
- id: renderer
- model: modelData
- notes: modelProxy.notes
- zoomer: scroller.zoomer
- selectionLocked: scroller.selectionLocked
- x: 0
- height: modelData.height
- property int visualIndex: DelegateModel.itemsIndex
-
- // paint "under" the vertical scrollbar, so that it always matches with the
- // timemarks
- width: flick.contentWidth
-
- Connections {
- target: timelineView
- onClearChildren: renderer.clearData()
- onSelect: {
- if (modelIndex === index || modelIndex === -1) {
- renderer.selectedItem = eventIndex;
- if (eventIndex !== -1)
- renderer.recenter();
- }
+ Column {
+ id: timelineView
+
+ signal clearChildren
+ signal select(int modelIndex, int eventIndex)
+
+ DelegateModel {
+ id: timelineModel
+ model: modelProxy.models
+ delegate: TimelineRenderer {
+ id: renderer
+ model: modelData
+ notes: modelProxy.notes
+ zoomer: flick.zoomer
+ selectionLocked: flick.selectionLocked
+ x: 0
+ height: modelData.height
+ property int visualIndex: DelegateModel.itemsIndex
+
+ // paint "under" the vertical scrollbar, so that it always matches with the
+ // timemarks
+ width: flick.contentWidth
+
+ Connections {
+ target: timelineView
+ onClearChildren: renderer.clearData()
+ onSelect: {
+ if (modelIndex === index || modelIndex === -1) {
+ renderer.selectedItem = eventIndex;
+ if (eventIndex !== -1)
+ renderer.recenter();
}
}
+ }
- function recenter() {
- if (modelData.endTime(selectedItem) < zoomer.rangeStart ||
- modelData.startTime(selectedItem) > zoomer.rangeEnd) {
-
- var newStart = Math.max((modelData.startTime(selectedItem) +
- modelData.endTime(selectedItem) -
- zoomer.rangeDuration) / 2, zoomer.traceStart);
- zoomer.setRange(newStart,
- Math.min(newStart + zoomer.rangeDuration, zoomer.traceEnd));
- }
+ function recenter() {
+ if (modelData.endTime(selectedItem) < zoomer.rangeStart ||
+ modelData.startTime(selectedItem) > zoomer.rangeEnd) {
- var row = modelData.row(selectedItem);
- var rowStart = modelData.rowOffset(row) + y;
- var rowEnd = rowStart + modelData.rowHeight(row);
- if (rowStart < flick.contentY || rowEnd - scroller.height > flick.contentY)
- flick.contentY = (rowStart + rowEnd - scroller.height) / 2;
+ var newStart = Math.max((modelData.startTime(selectedItem) +
+ modelData.endTime(selectedItem) -
+ zoomer.rangeDuration) / 2, zoomer.traceStart);
+ zoomer.setRange(newStart,
+ Math.min(newStart + zoomer.rangeDuration, zoomer.traceEnd));
}
- onSelectedItemChanged: scroller.propagateSelection(index, selectedItem);
+ var row = modelData.row(selectedItem);
+ var rowStart = modelData.rowOffset(row) + y;
+ var rowEnd = rowStart + modelData.rowHeight(row);
+ if (rowStart < flick.contentY || rowEnd - flick.height > flick.contentY)
+ flick.contentY = (rowStart + rowEnd - flick.height) / 2;
+ }
+
+ onSelectedItemChanged: flick.propagateSelection(index, selectedItem);
- Connections {
- target: model
- onDetailsChanged: {
- if (selectedItem != -1) {
- scroller.propagateSelection(-1, -1);
- scroller.propagateSelection(index, selectedItem);
- }
+ Connections {
+ target: model
+ onDetailsChanged: {
+ if (selectedItem != -1) {
+ flick.propagateSelection(-1, -1);
+ flick.propagateSelection(index, selectedItem);
}
}
}
}
+ }
- Repeater {
- id: repeater
- model: timelineModel
- }
+ Repeater {
+ id: repeater
+ model: timelineModel
}
}
}
diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt
index 5863c35d1f..5cc79c3182 100644
--- a/src/libs/utils/CMakeLists.txt
+++ b/src/libs/utils/CMakeLists.txt
@@ -1,6 +1,9 @@
if (IDE_LIBEXEC_PATH AND IDE_BIN_PATH)
- file(RELATIVE_PATH RELATIVE_TOOLS_PATH
- "${CMAKE_INSTALL_PREFIX}/${IDE_BIN_PATH}" "${CMAKE_INSTALL_PREFIX}/${IDE_LIBEXEC_PATH}")
+ get_filename_component(bin_path
+ "${CMAKE_INSTALL_PREFIX}/${IDE_BIN_PATH}" ABSOLUTE "${CMAKE_BINARY_DIR}")
+ get_filename_component(libexec_path
+ "${CMAKE_INSTALL_PREFIX}/${IDE_LIBEXEC_PATH}" ABSOLUTE "${CMAKE_BINARY_DIR}")
+ file(RELATIVE_PATH RELATIVE_TOOLS_PATH "${bin_path}" "${libexec_path}")
else()
message(WARNING "IDE_LIBEXEC_PATH or IDE_BIN_PATH undefined when calculating tools path")
set(RELATIVE_TOOLS_PATH "")
@@ -40,8 +43,14 @@ add_qtc_library(Utils
elfreader.cpp elfreader.h
elidinglabel.cpp elidinglabel.h
environment.cpp environment.h
+ environmentfwd.h
environmentdialog.cpp environmentdialog.h
environmentmodel.cpp environmentmodel.h
+ namevaluedictionary.cpp namevaluedictionary.h
+ namevalueitem.cpp namevalueitem.h
+ namevaluemodel.cpp namevaluemodel.h
+ namevaluesdialog.cpp namevaluesdialog.h
+ namevaluevalidator.cpp namevaluevalidator.h
execmenu.cpp execmenu.h
executeondestruction.h
fadingindicator.cpp fadingindicator.h
@@ -84,6 +93,11 @@ add_qtc_library(Utils
mimetypes/mimeprovider.cpp mimetypes/mimeprovider_p.h
mimetypes/mimetype.cpp mimetypes/mimetype.h mimetypes/mimetype_p.h
mimetypes/mimetypeparser.cpp mimetypes/mimetypeparser_p.h
+ namevaluedictionary.cpp namevaluedictionary.h
+ namevalueitem.cpp namevalueitem.h
+ namevaluemodel.cpp namevaluemodel.h
+ namevaluesdialog.cpp namevaluesdialog.h
+ namevaluevalidator.cpp namevaluevalidator.h
navigationtreeview.cpp navigationtreeview.h
networkaccessmanager.cpp networkaccessmanager.h
newclasswidget.cpp newclasswidget.h newclasswidget.ui
diff --git a/src/libs/utils/algorithm.h b/src/libs/utils/algorithm.h
index 237863c573..1e821a4bcb 100644
--- a/src/libs/utils/algorithm.h
+++ b/src/libs/utils/algorithm.h
@@ -407,6 +407,20 @@ bool allOf(const T &container, F predicate)
return std::all_of(std::begin(container), std::end(container), predicate);
}
+// allOf taking a member function pointer
+template<typename T, typename R, typename S>
+bool allOf(const T &container, R (S::*predicate)() const)
+{
+ return std::all_of(std::begin(container), std::end(container), std::mem_fn(predicate));
+}
+
+// allOf taking a member pointer
+template<typename T, typename R, typename S>
+bool allOf(const T &container, R S::*member)
+{
+ return std::all_of(std::begin(container), std::end(container), std::mem_fn(member));
+}
+
//////////////////
// erase
/////////////////
@@ -885,6 +899,8 @@ std::tuple<C, C> partition(const C &container, F predicate)
{
C hit;
C miss;
+ reserve(hit, container.size());
+ reserve(miss, container.size());
auto hitIns = inserter(hit);
auto missIns = inserter(miss);
for (auto i : container) {
diff --git a/src/libs/utils/buildablehelperlibrary.cpp b/src/libs/utils/buildablehelperlibrary.cpp
index 33ba624dc4..d1978a1e4f 100644
--- a/src/libs/utils/buildablehelperlibrary.cpp
+++ b/src/libs/utils/buildablehelperlibrary.cpp
@@ -46,7 +46,7 @@ QString BuildableHelperLibrary::qtChooserToQmakePath(const QString &path)
const QString toolDir = QLatin1String("QTTOOLDIR=\"");
SynchronousProcess proc;
proc.setTimeoutS(1);
- SynchronousProcessResponse response = proc.runBlocking(path, QStringList(QLatin1String("-print-env")));
+ SynchronousProcessResponse response = proc.runBlocking({FilePath::fromString(path), {"-print-env"}});
if (response.result != SynchronousProcessResponse::Finished)
return QString();
const QString output = response.stdOut();
@@ -130,7 +130,7 @@ QString BuildableHelperLibrary::qtVersionForQMake(const QString &qmakePath)
SynchronousProcess qmake;
qmake.setTimeoutS(5);
- SynchronousProcessResponse response = qmake.runBlocking(qmakePath, QStringList(QLatin1String("--version")));
+ SynchronousProcessResponse response = qmake.runBlocking({FilePath::fromString(qmakePath), {"--version"}});
if (response.result != SynchronousProcessResponse::Finished) {
qWarning() << response.exitMessage(qmakePath, 5);
return QString();
diff --git a/src/libs/utils/consoleprocess.h b/src/libs/utils/consoleprocess.h
index 3dec9838c3..1ae0bb4496 100644
--- a/src/libs/utils/consoleprocess.h
+++ b/src/libs/utils/consoleprocess.h
@@ -35,8 +35,9 @@ class QSettings;
QT_END_NAMESPACE
namespace Utils {
+
class Environment;
-struct ConsoleProcessPrivate;
+class CommandLine;
class QTCREATOR_UTILS_EXPORT TerminalCommand
{
@@ -61,6 +62,8 @@ public:
ConsoleProcess(QObject *parent = nullptr);
~ConsoleProcess() override;
+ void setCommand(const Utils::CommandLine &command);
+
void setWorkingDirectory(const QString &dir);
QString workingDirectory() const;
@@ -70,9 +73,8 @@ public:
QProcess::ProcessError error() const;
QString errorString() const;
- enum class MetaCharMode { Abort, Ignore };
- bool start(const QString &program, const QString &args,
- MetaCharMode metaCharMode = MetaCharMode::Abort);
+ bool start();
+
public slots:
void stop();
@@ -158,7 +160,7 @@ private:
void cleanupInferior();
#endif
- ConsoleProcessPrivate *d;
+ struct ConsoleProcessPrivate *d;
};
} //namespace Utils
diff --git a/src/libs/utils/consoleprocess_p.h b/src/libs/utils/consoleprocess_p.h
index 5d8d193493..b32be8c586 100644
--- a/src/libs/utils/consoleprocess_p.h
+++ b/src/libs/utils/consoleprocess_p.h
@@ -53,7 +53,7 @@ struct ConsoleProcessPrivate {
Environment m_environment;
qint64 m_appPid = 0;
int m_appCode;
- QString m_executable;
+ CommandLine m_commandLine;
QProcess::ExitStatus m_appStatus;
QLocalServer m_stubServer;
QLocalSocket *m_stubSocket = nullptr;
diff --git a/src/libs/utils/consoleprocess_unix.cpp b/src/libs/utils/consoleprocess_unix.cpp
index 43afffa0a2..fe83b95f58 100644
--- a/src/libs/utils/consoleprocess_unix.cpp
+++ b/src/libs/utils/consoleprocess_unix.cpp
@@ -60,12 +60,17 @@ qint64 ConsoleProcess::applicationMainThreadID() const
return -1;
}
+void ConsoleProcess::setCommand(const Utils::CommandLine &command)
+{
+ d->m_commandLine = command;
+}
+
void ConsoleProcess::setSettings(QSettings *settings)
{
d->m_settings = settings;
}
-bool ConsoleProcess::start(const QString &program, const QString &args, MetaCharMode metaCharMode)
+bool ConsoleProcess::start()
{
if (isRunning())
return false;
@@ -74,12 +79,16 @@ bool ConsoleProcess::start(const QString &program, const QString &args, MetaChar
d->m_error = QProcess::UnknownError;
QtcProcess::SplitError perr;
- QtcProcess::Arguments pargs = QtcProcess::prepareArgs(args, &perr, HostOsInfo::hostOs(),
- &d->m_environment, &d->m_workingDir,
- metaCharMode == MetaCharMode::Abort);
+ QtcProcess::Arguments pargs = QtcProcess::prepareArgs(d->m_commandLine.arguments(),
+ &perr,
+ HostOsInfo::hostOs(),
+ &d->m_environment,
+ &d->m_workingDir,
+ d->m_commandLine.metaCharMode()
+ == CommandLine::MetaCharMode::Abort);
QString pcmd;
if (perr == QtcProcess::SplitOk) {
- pcmd = program;
+ pcmd = d->m_commandLine.executable().toString();
} else {
if (perr != QtcProcess::FoundMeta) {
emitError(QProcess::FailedToStart, tr("Quoting error in command."));
@@ -93,7 +102,8 @@ bool ConsoleProcess::start(const QString &program, const QString &args, MetaChar
}
pcmd = QLatin1String("/bin/sh");
pargs = QtcProcess::Arguments::createUnixArgs(
- QStringList({"-c", (QtcProcess::quoteArg(program) + ' ' + args)}));
+ {"-c", (QtcProcess::quoteArg(d->m_commandLine.executable().toString())
+ + ' ' + d->m_commandLine.arguments())});
}
QtcProcess::SplitError qerr;
@@ -167,7 +177,6 @@ bool ConsoleProcess::start(const QString &program, const QString &args, MetaChar
connect(d->m_stubConnectTimer, &QTimer::timeout, this, &ConsoleProcess::stop);
d->m_stubConnectTimer->setSingleShot(true);
d->m_stubConnectTimer->start(10000);
- d->m_executable = program;
return true;
}
@@ -286,7 +295,7 @@ void ConsoleProcess::readStubOutput()
if (out.startsWith("err:chdir ")) {
emitError(QProcess::FailedToStart, msgCannotChangeToWorkDir(workingDirectory(), errorMsg(out.mid(10).toInt())));
} else if (out.startsWith("err:exec ")) {
- emitError(QProcess::FailedToStart, msgCannotExecute(d->m_executable, errorMsg(out.mid(9).toInt())));
+ emitError(QProcess::FailedToStart, msgCannotExecute(d->m_commandLine.executable().toString(), errorMsg(out.mid(9).toInt())));
} else if (out.startsWith("spid ")) {
delete d->m_tempFile;
d->m_tempFile = nullptr;
diff --git a/src/libs/utils/consoleprocess_win.cpp b/src/libs/utils/consoleprocess_win.cpp
index be65c42cc8..fdead4db85 100644
--- a/src/libs/utils/consoleprocess_win.cpp
+++ b/src/libs/utils/consoleprocess_win.cpp
@@ -46,15 +46,18 @@ ConsoleProcess::ConsoleProcess(QObject *parent) :
this, &ConsoleProcess::stubConnectionAvailable);
}
+void ConsoleProcess::setCommand(const Utils::CommandLine &command)
+{
+ d->m_commandLine = command;
+}
+
qint64 ConsoleProcess::applicationMainThreadID() const
{
return d->m_appMainThreadId;
}
-bool ConsoleProcess::start(const QString &program, const QString &args, MetaCharMode metaCharMode)
+bool ConsoleProcess::start()
{
- Q_UNUSED(metaCharMode);
-
if (isRunning())
return false;
@@ -64,11 +67,13 @@ bool ConsoleProcess::start(const QString &program, const QString &args, MetaChar
QString pcmd;
QString pargs;
if (d->m_mode != Run) { // The debugger engines already pre-process the arguments.
- pcmd = program;
- pargs = args;
+ pcmd = d->m_commandLine.executable().toString();
+ pargs = d->m_commandLine.arguments();
} else {
QtcProcess::Arguments outArgs;
- QtcProcess::prepareCommand(program, args, &pcmd, &outArgs, OsTypeWindows,
+ QtcProcess::prepareCommand(d->m_commandLine.executable().toString(),
+ d->m_commandLine.arguments(),
+ &pcmd, &outArgs, OsTypeWindows,
&d->m_environment, &d->m_workingDir);
pargs = outArgs.toWindowsArgs();
}
@@ -209,7 +214,7 @@ void ConsoleProcess::readStubOutput()
if (out.startsWith("err:chdir ")) {
emitError(QProcess::FailedToStart, msgCannotChangeToWorkDir(workingDirectory(), winErrorMessage(out.mid(10).toInt())));
} else if (out.startsWith("err:exec ")) {
- emitError(QProcess::FailedToStart, msgCannotExecute(d->m_executable, winErrorMessage(out.mid(9).toInt())));
+ emitError(QProcess::FailedToStart, msgCannotExecute(d->m_commandLine.executable().toUserOutput(), winErrorMessage(out.mid(9).toInt())));
} else if (out.startsWith("thread ")) { // Windows only
d->m_appMainThreadId = out.mid(7).toLongLong();
} else if (out.startsWith("pid ")) {
diff --git a/src/libs/utils/detailsbutton.cpp b/src/libs/utils/detailsbutton.cpp
index 329975afdb..abceff7b2d 100644
--- a/src/libs/utils/detailsbutton.cpp
+++ b/src/libs/utils/detailsbutton.cpp
@@ -200,3 +200,61 @@ QPixmap DetailsButton::cacheRendering(const QSize &size, bool checked)
style()->drawPrimitive(checked ? QStyle::PE_IndicatorArrowUp : QStyle::PE_IndicatorArrowDown, &arrowOpt, &p, this);
return pixmap;
}
+
+ExpandButton::ExpandButton(QWidget *parent) : QAbstractButton(parent)
+{
+ setCheckable(true);
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
+}
+
+QSize ExpandButton::sizeHint() const
+{
+ return {fontMetrics().horizontalAdvance(text()) + 26, HostOsInfo::isMacHost() ? 34 : 22};
+}
+
+void ExpandButton::paintEvent(QPaintEvent *e)
+{
+ QWidget::paintEvent(e);
+ QPainter p(this);
+
+ QPixmap &pixmap = isChecked() ? m_checkedPixmap : m_uncheckedPixmap;
+ if (pixmap.isNull() || pixmap.size() / pixmap.devicePixelRatio() != contentsRect().size())
+ pixmap = cacheRendering();
+ p.drawPixmap(contentsRect(), pixmap);
+
+ if (isDown()) {
+ p.setPen(Qt::NoPen);
+ p.setBrush(QColor(0, 0, 0, 20));
+ p.drawRoundedRect(rect().adjusted(1, 1, -1, -1), 1, 1);
+ }
+ if (hasFocus()) {
+ QStyleOptionFocusRect option;
+ option.initFrom(this);
+ style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &p, this);
+ }
+}
+
+QPixmap ExpandButton::cacheRendering()
+{
+ const QSize size = contentsRect().size();
+ const qreal pixelRatio = devicePixelRatio();
+ QPixmap pixmap(size * pixelRatio);
+ pixmap.setDevicePixelRatio(pixelRatio);
+ pixmap.fill(Qt::transparent);
+ QPainter p(&pixmap);
+ p.setRenderHint(QPainter::Antialiasing, true);
+ p.translate(0.5, 0.5);
+ p.setPen(Qt::NoPen);
+ p.drawRoundedRect(0, 0, size.width(), size.height(), 1, 1);
+ int arrowsize = 15;
+ QStyleOption arrowOpt;
+ arrowOpt.initFrom(this);
+ QPalette pal = arrowOpt.palette;
+ pal.setBrush(QPalette::All, QPalette::Text, QColor(0, 0, 0));
+ arrowOpt.rect = QRect(size.width() - arrowsize - 6, height() / 2 - arrowsize / 2,
+ arrowsize, arrowsize);
+ arrowOpt.palette = pal;
+ style()->drawPrimitive(isChecked() ? QStyle::PE_IndicatorArrowUp
+ : QStyle::PE_IndicatorArrowDown, &arrowOpt, &p, this);
+ return pixmap;
+}
diff --git a/src/libs/utils/detailsbutton.h b/src/libs/utils/detailsbutton.h
index f66fa2ad4b..dabbe75918 100644
--- a/src/libs/utils/detailsbutton.h
+++ b/src/libs/utils/detailsbutton.h
@@ -78,4 +78,22 @@ private:
QPixmap m_uncheckedPixmap;
float m_fader;
};
+
+class QTCREATOR_UTILS_EXPORT ExpandButton : public QAbstractButton
+{
+ Q_OBJECT
+
+public:
+ ExpandButton(QWidget *parent = nullptr);
+
+private:
+ void paintEvent(QPaintEvent *e) override;
+ QSize sizeHint() const override;
+
+ QPixmap cacheRendering();
+
+ QPixmap m_checkedPixmap;
+ QPixmap m_uncheckedPixmap;
+};
+
} // namespace Utils
diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp
index d61a75ebf7..1d8498ef0d 100644
--- a/src/libs/utils/environment.cpp
+++ b/src/libs/utils/environment.cpp
@@ -39,11 +39,12 @@ Q_GLOBAL_STATIC_WITH_ARGS(Utils::Environment, staticSystemEnvironment,
Q_GLOBAL_STATIC(QVector<Utils::EnvironmentProvider>, environmentProviders)
-static QMap<QString, QString>::iterator findKey(QMap<QString, QString> &input, Utils::OsType osType,
- const QString &key)
+namespace Utils {
+
+static NameValueMap::iterator findKey(NameValueMap &input, Utils::OsType osType, const QString &key)
{
- const Qt::CaseSensitivity casing
- = (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive : Qt::CaseSensitive;
+ const Qt::CaseSensitivity casing = (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive
+ : Qt::CaseSensitive;
for (auto it = input.begin(); it != input.end(); ++it) {
if (key.compare(it.key(), casing) == 0)
return it;
@@ -51,12 +52,12 @@ static QMap<QString, QString>::iterator findKey(QMap<QString, QString> &input, U
return input.end();
}
-static QMap<QString, QString>::const_iterator findKey(const QMap<QString, QString> &input,
- Utils::OsType osType,
- const QString &key)
+static NameValueMap::const_iterator findKey(const NameValueMap &input,
+ Utils::OsType osType,
+ const QString &key)
{
- const Qt::CaseSensitivity casing
- = (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive : Qt::CaseSensitive;
+ const Qt::CaseSensitivity casing = (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive
+ : Qt::CaseSensitive;
for (auto it = input.constBegin(); it != input.constEnd(); ++it) {
if (key.compare(it.key(), casing) == 0)
return it;
@@ -64,198 +65,6 @@ static QMap<QString, QString>::const_iterator findKey(const QMap<QString, QStrin
return input.constEnd();
}
-namespace Utils {
-
-enum : char
-{
-#ifdef Q_OS_WIN
- pathSepC = ';'
-#else
- pathSepC = ':'
-#endif
-};
-
-void EnvironmentItem::sort(QList<EnvironmentItem> *list)
-{
- Utils::sort(*list, &EnvironmentItem::name);
-}
-
-QList<EnvironmentItem> EnvironmentItem::fromStringList(const QStringList &list)
-{
- QList<EnvironmentItem> result;
- for (const QString &string : list) {
- int pos = string.indexOf('=', 1);
- if (pos == -1)
- result.append(EnvironmentItem(string, QString(), EnvironmentItem::Unset));
- else
- result.append(EnvironmentItem(string.left(pos), string.mid(pos + 1)));
- }
- return result;
-}
-
-QStringList EnvironmentItem::toStringList(const QList<EnvironmentItem> &list)
-{
- return Utils::transform(list, [](const EnvironmentItem &item) {
- if (item.operation == EnvironmentItem::Unset)
- return QString(item.name);
- return QString(item.name + '=' + item.value);
- });
-}
-
-QList<EnvironmentItem> EnvironmentItem::itemsFromVariantList(const QVariantList &list)
-{
- return Utils::transform(list, [](const QVariant &item) {
- return itemFromVariantList(item.toList());
- });
-}
-
-QVariantList EnvironmentItem::toVariantList(const QList<EnvironmentItem> &list)
-{
- return Utils::transform(list, [](const EnvironmentItem &item) {
- return QVariant(toVariantList(item));
- });
-}
-
-EnvironmentItem EnvironmentItem::itemFromVariantList(const QVariantList &list)
-{
- QTC_ASSERT(list.size() == 3, return EnvironmentItem("", ""));
- QString name = list.value(0).toString();
- Operation operation = Operation(list.value(1).toInt());
- QString value = list.value(2).toString();
- return EnvironmentItem(name, value, operation);
-}
-
-QVariantList EnvironmentItem::toVariantList(const EnvironmentItem &item)
-{
- return QVariantList() << item.name << item.operation << item.value;
-}
-
-static QString expand(const Environment *e, QString value)
-{
- int replaceCount = 0;
- for (int i = 0; i < value.size(); ++i) {
- if (value.at(i) == '$') {
- if ((i + 1) < value.size()) {
- const QChar &c = value.at(i+1);
- int end = -1;
- if (c == '(')
- end = value.indexOf(')', i);
- else if (c == '{')
- end = value.indexOf('}', i);
- if (end != -1) {
- const QString &name = value.mid(i + 2, end - i - 2);
- Environment::const_iterator it = e->constFind(name);
- if (it != e->constEnd())
- value.replace(i, end - i + 1, it.value());
- ++replaceCount;
- QTC_ASSERT(replaceCount < 100, break);
- }
- }
- }
- }
- return value;
-}
-
-QDebug operator<<(QDebug debug, const EnvironmentItem &i)
-{
- QDebugStateSaver saver(debug);
- debug.noquote();
- debug.nospace();
- debug << "EnvironmentItem(";
- switch (i.operation) {
- case EnvironmentItem::Set:
- debug << "set \"" << i.name << "\" to \"" << i.value << '"';
- break;
- case EnvironmentItem::Unset:
- debug << "unset \"" << i.name << '"';
- break;
- case EnvironmentItem::Prepend:
- debug << "prepend to \"" << i.name << "\":\"" << i.value << '"';
- break;
- case EnvironmentItem::Append:
- debug << "append to \"" << i.name << "\":\"" << i.value << '"';
- break;
- }
- debug << ')';
- return debug;
-}
-
-void EnvironmentItem::apply(Environment *e, Operation op) const
-{
- switch (op) {
- case Set:
- e->set(name, expand(e, value));
- break;
- case Unset:
- e->unset(name);
- break;
- case Prepend: {
- const Environment::const_iterator it = e->constFind(name);
- if (it != e->constEnd()) {
- QString v = it.value();
- const QChar pathSep{QLatin1Char(pathSepC)};
- int sepCount = 0;
- if (v.startsWith(pathSep))
- ++sepCount;
- if (value.endsWith(pathSep))
- ++sepCount;
- if (sepCount == 2)
- v.remove(0, 1);
- else if (sepCount == 0)
- v.prepend(pathSep);
- v.prepend(expand(e, value));
- e->set(name, v);
- } else {
- apply(e, Set);
- }
- }
- break;
- case Append: {
- const Environment::const_iterator it = e->constFind(name);
- if (it != e->constEnd()) {
- QString v = it.value();
- const QChar pathSep{QLatin1Char(pathSepC)};
- int sepCount = 0;
- if (v.endsWith(pathSep))
- ++sepCount;
- if (value.startsWith(pathSep))
- ++sepCount;
- if (sepCount == 2)
- v.chop(1);
- else if (sepCount == 0)
- v.append(pathSep);
- v.append(expand(e, value));
- e->set(name, v);
- } else {
- apply(e, Set);
- }
- }
- break;
- }
-}
-
-Environment::Environment(const QStringList &env, OsType osType) : m_osType(osType)
-{
- for (const QString &s : env) {
- int i = s.indexOf('=', 1);
- if (i >= 0) {
- const QString key = s.left(i);
- if (!key.contains('=')) {
- const QString value = s.mid(i + 1);
- set(key, value);
- }
- }
- }
-}
-
-QStringList Environment::toStringList() const
-{
- QStringList result;
- for (auto it = m_values.constBegin(); it != m_values.constEnd(); ++it)
- result.append(it.key() + '=' + it.value());
- return result;
-}
-
QProcessEnvironment Environment::toProcessEnvironment() const
{
QProcessEnvironment result;
@@ -264,27 +73,21 @@ QProcessEnvironment Environment::toProcessEnvironment() const
return result;
}
-void Environment::set(const QString &key, const QString &value)
+void Environment::appendOrSetPath(const QString &value)
{
- QTC_ASSERT(!key.contains('='), return);
- auto it = findKey(m_values, m_osType, key);
- if (it == m_values.end())
- m_values.insert(key, value);
- else
- it.value() = value;
+ appendOrSet("PATH", QDir::toNativeSeparators(value),
+ QString(OsSpecificAspects::pathListSeparator(m_osType)));
}
-void Environment::unset(const QString &key)
+void Environment::prependOrSetPath(const QString &value)
{
- QTC_ASSERT(!key.contains('='), return);
- auto it = findKey(m_values, m_osType, key);
- if (it != m_values.end())
- m_values.erase(it);
+ prependOrSet("PATH", QDir::toNativeSeparators(value),
+ QString(OsSpecificAspects::pathListSeparator(m_osType)));
}
void Environment::appendOrSet(const QString &key, const QString &value, const QString &sep)
{
- QTC_ASSERT(!key.contains('='), return);
+ QTC_ASSERT(!key.contains('='), return );
auto it = findKey(m_values, m_osType, key);
if (it == m_values.end()) {
m_values.insert(key, value);
@@ -296,9 +99,9 @@ void Environment::appendOrSet(const QString &key, const QString &value, const QS
}
}
-void Environment::prependOrSet(const QString&key, const QString &value, const QString &sep)
+void Environment::prependOrSet(const QString &key, const QString &value, const QString &sep)
{
- QTC_ASSERT(!key.contains('='), return);
+ QTC_ASSERT(!key.contains('='), return );
auto it = findKey(m_values, m_osType, key);
if (it == m_values.end()) {
m_values.insert(key, value);
@@ -310,18 +113,6 @@ void Environment::prependOrSet(const QString&key, const QString &value, const QS
}
}
-void Environment::appendOrSetPath(const QString &value)
-{
- appendOrSet("PATH", QDir::toNativeSeparators(value),
- QString(OsSpecificAspects::pathListSeparator(m_osType)));
-}
-
-void Environment::prependOrSetPath(const QString &value)
-{
- prependOrSet("PATH", QDir::toNativeSeparators(value),
- QString(OsSpecificAspects::pathListSeparator(m_osType)));
-}
-
void Environment::prependOrSetLibrarySearchPath(const QString &value)
{
switch (m_osType) {
@@ -387,11 +178,6 @@ void Environment::setupEnglishOutput(QStringList *environment)
*environment = env.toStringList();
}
-void Environment::clear()
-{
- m_values.clear();
-}
-
FilePath Environment::searchInDirectory(const QStringList &execs, const FilePath &directory,
QSet<FilePath> &alreadyChecked) const
{
@@ -489,127 +275,17 @@ FilePath Environment::searchInPath(const QString &executable,
FilePathList Environment::path() const
{
- const QStringList pathComponents = value("PATH")
- .split(OsSpecificAspects::pathListSeparator(m_osType), QString::SkipEmptyParts);
- return Utils::transform(pathComponents, &FilePath::fromUserInput);
-}
-
-QString Environment::value(const QString &key) const
-{
- const auto it = findKey(m_values, m_osType, key);
- return it != m_values.end() ? it.value() : QString();
-}
-
-QString Environment::key(Environment::const_iterator it) const
-{
- return it.key();
-}
-
-QString Environment::value(Environment::const_iterator it) const
-{
- return it.value();
-}
-
-Environment::const_iterator Environment::constBegin() const
-{
- return m_values.constBegin();
-}
-
-Environment::const_iterator Environment::constEnd() const
-{
- return m_values.constEnd();
-}
-
-Environment::const_iterator Environment::constFind(const QString &name) const
-{
- return findKey(m_values, m_osType, name);
-}
-
-int Environment::size() const
-{
- return m_values.size();
+ return pathListValue("PATH");
}
-void Environment::modify(const QList<EnvironmentItem> & list)
+FilePathList Environment::pathListValue(const QString &varName) const
{
- Environment resultEnvironment = *this;
- for (const EnvironmentItem &item : list)
- item.apply(&resultEnvironment);
- *this = resultEnvironment;
-}
-
-QList<EnvironmentItem> Environment::diff(const Environment &other, bool checkAppendPrepend) const
-{
- QMap<QString, QString>::const_iterator thisIt = constBegin();
- QMap<QString, QString>::const_iterator otherIt = other.constBegin();
-
- QList<EnvironmentItem> result;
- while (thisIt != constEnd() || otherIt != other.constEnd()) {
- if (thisIt == constEnd()) {
- result.append(EnvironmentItem(otherIt.key(), otherIt.value()));
- ++otherIt;
- } else if (otherIt == other.constEnd()) {
- result.append(EnvironmentItem(thisIt.key(), QString(), EnvironmentItem::Unset));
- ++thisIt;
- } else if (thisIt.key() < otherIt.key()) {
- result.append(EnvironmentItem(thisIt.key(), QString(), EnvironmentItem::Unset));
- ++thisIt;
- } else if (thisIt.key() > otherIt.key()) {
- result.append(EnvironmentItem(otherIt.key(), otherIt.value()));
- ++otherIt;
- } else {
- const QString &oldValue = thisIt.value();
- const QString &newValue = otherIt.value();
- if (oldValue != newValue) {
- if (checkAppendPrepend && newValue.startsWith(oldValue)) {
- QString appended = newValue.right(newValue.size() - oldValue.size());
- if (appended.startsWith(QLatin1Char(pathSepC)))
- appended.remove(0, 1);
- result.append(EnvironmentItem(otherIt.key(), appended,
- EnvironmentItem::Append));
- } else if (checkAppendPrepend && newValue.endsWith(oldValue)) {
- QString prepended = newValue.left(newValue.size() - oldValue.size());
- if (prepended.endsWith(QLatin1Char(pathSepC)))
- prepended.chop(1);
- result.append(EnvironmentItem(otherIt.key(), prepended,
- EnvironmentItem::Prepend));
- } else {
- result.append(EnvironmentItem(otherIt.key(), newValue));
- }
- }
- ++otherIt;
- ++thisIt;
- }
- }
- return result;
-}
-
-bool Environment::hasKey(const QString &key) const
-{
- return m_values.contains(key);
-}
-
-OsType Environment::osType() const
-{
- return m_osType;
-}
-
-QString Environment::userName() const
-{
- return value(QString::fromLatin1(m_osType == OsTypeWindows ? "USERNAME" : "USER"));
-}
-
-bool Environment::operator!=(const Environment &other) const
-{
- return !(*this == other);
-}
-
-bool Environment::operator==(const Environment &other) const
-{
- return m_osType == other.m_osType && m_values == other.m_values;
+ const QStringList pathComponents = value(varName)
+ .split(OsSpecificAspects::pathListSeparator(m_osType), QString::SkipEmptyParts);
+ return transform(pathComponents, &FilePath::fromUserInput);
}
-void Environment::modifySystemEnvironment(const QList<EnvironmentItem> &list)
+void Environment::modifySystemEnvironment(const EnvironmentItems &list)
{
staticSystemEnvironment->modify(list);
}
diff --git a/src/libs/utils/environment.h b/src/libs/utils/environment.h
index f1e957ab6d..73c883268e 100644
--- a/src/libs/utils/environment.h
+++ b/src/libs/utils/environment.h
@@ -27,8 +27,9 @@
#include "fileutils.h"
#include "hostosinfo.h"
+#include "namevaluedictionary.h"
+#include "namevalueitem.h"
#include "optional.h"
-#include "utils_global.h"
#include <QMap>
#include <QStringList>
@@ -39,71 +40,18 @@ QT_FORWARD_DECLARE_CLASS(QDebug)
QT_FORWARD_DECLARE_CLASS(QProcessEnvironment)
namespace Utils {
-class Environment;
-class QTCREATOR_UTILS_EXPORT EnvironmentItem
+class QTCREATOR_UTILS_EXPORT Environment final : public NameValueDictionary
{
public:
- enum Operation { Set, Unset, Prepend, Append };
+ using NameValueDictionary::NameValueDictionary;
- EnvironmentItem(const QString &n, const QString &v, Operation op = Set)
- : name(n), value(v), operation(op)
- {}
-
- void apply(Environment *e) const { apply(e, operation); }
-
- QString name;
- QString value;
- Operation operation;
-
- bool operator==(const EnvironmentItem &other) const
- {
- return operation == other.operation && name == other.name && value == other.value;
- }
-
- bool operator!=(const EnvironmentItem &other) const
- {
- return !(*this == other);
- }
-
- static void sort(QList<EnvironmentItem> *list);
- static QList<EnvironmentItem> fromStringList(const QStringList &list);
- static QStringList toStringList(const QList<EnvironmentItem> &list);
- static QList<EnvironmentItem> itemsFromVariantList(const QVariantList &list);
- static QVariantList toVariantList(const QList<EnvironmentItem> &list);
- static EnvironmentItem itemFromVariantList(const QVariantList &list);
- static QVariantList toVariantList(const EnvironmentItem &item);
-
-private:
- void apply(Environment *e, Operation op) const;
-};
-
-QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug debug, const EnvironmentItem &i);
-
-class QTCREATOR_UTILS_EXPORT Environment
-{
-public:
- using const_iterator = QMap<QString, QString>::const_iterator;
-
- explicit Environment(OsType osType = HostOsInfo::hostOs()) : m_osType(osType) {}
- explicit Environment(const QStringList &env, OsType osType = HostOsInfo::hostOs());
static Environment systemEnvironment();
static void setupEnglishOutput(Environment *environment);
static void setupEnglishOutput(QProcessEnvironment *environment);
static void setupEnglishOutput(QStringList *environment);
- QStringList toStringList() const;
QProcessEnvironment toProcessEnvironment() const;
- QString value(const QString &key) const;
- void set(const QString &key, const QString &value);
- void unset(const QString &key);
- void modify(const QList<EnvironmentItem> &list);
- /// Return the Environment changes necessary to modify this into the other environment.
- QList<EnvironmentItem> diff(const Environment &other, bool checkAppendPrepend = false) const;
- bool hasKey(const QString &key) const;
- OsType osType() const;
-
- QString userName() const;
void appendOrSet(const QString &key, const QString &value, const QString &sep = QString());
void prependOrSet(const QString &key, const QString &value, const QString &sep = QString());
@@ -114,22 +62,13 @@ public:
void prependOrSetLibrarySearchPath(const QString &value);
void prependOrSetLibrarySearchPaths(const QStringList &values);
- void clear();
- int size() const;
-
- QString key(Environment::const_iterator it) const;
- QString value(Environment::const_iterator it) const;
-
- Environment::const_iterator constBegin() const;
- Environment::const_iterator constEnd() const;
- Environment::const_iterator constFind(const QString &name) const;
-
using PathFilter = std::function<bool(const FilePath &)>;
FilePath searchInPath(const QString &executable,
const FilePathList &additionalDirs = FilePathList(),
const PathFilter &func = PathFilter()) const;
FilePathList path() const;
+ FilePathList pathListValue(const QString &varName) const;
QStringList appendExeExtensions(const QString &executable) const;
bool isSameExecutable(const QString &exe1, const QString &exe2) const;
@@ -138,16 +77,11 @@ public:
FilePath expandVariables(const FilePath &input) const;
QStringList expandVariables(const QStringList &input) const;
- bool operator!=(const Environment &other) const;
- bool operator==(const Environment &other) const;
-
- static void modifySystemEnvironment(const QList<EnvironmentItem> &list); // use with care!!!
+ static void modifySystemEnvironment(const EnvironmentItems &list); // use with care!!!
private:
FilePath searchInDirectory(const QStringList &execs, const FilePath &directory,
QSet<FilePath> &alreadyChecked) const;
- QMap<QString, QString> m_values;
- OsType m_osType;
};
class QTCREATOR_UTILS_EXPORT EnvironmentProvider
diff --git a/src/libs/utils/environmentdialog.cpp b/src/libs/utils/environmentdialog.cpp
index 07e6155c7d..e9c9fd620f 100644
--- a/src/libs/utils/environmentdialog.cpp
+++ b/src/libs/utils/environmentdialog.cpp
@@ -35,144 +35,19 @@
namespace Utils {
-namespace Internal {
-
-static QList<EnvironmentItem> cleanUp(
- const QList<EnvironmentItem> &items)
-{
- QList<EnvironmentItem> uniqueItems;
- QSet<QString> uniqueSet;
- for (int i = items.count() - 1; i >= 0; i--) {
- EnvironmentItem item = items.at(i);
- if (HostOsInfo::isWindowsHost())
- item.name = item.name.toUpper();
- const QString &itemName = item.name;
- QString emptyName = itemName;
- emptyName.remove(QLatin1Char(' '));
- if (!emptyName.isEmpty() && !uniqueSet.contains(itemName)) {
- uniqueItems.prepend(item);
- uniqueSet.insert(itemName);
- }
- }
- return uniqueItems;
-}
-
-class EnvironmentItemsWidget : public QWidget
-{
- Q_OBJECT
-public:
- explicit EnvironmentItemsWidget(QWidget *parent = nullptr);
-
- void setEnvironmentItems(const QList<EnvironmentItem> &items);
- QList<EnvironmentItem> environmentItems() const;
-
- void setPlaceholderText(const QString &text);
-
-private:
- QPlainTextEdit *m_editor;
-};
-
-EnvironmentItemsWidget::EnvironmentItemsWidget(QWidget *parent) :
- QWidget(parent)
-{
- m_editor = new QPlainTextEdit(this);
- auto layout = new QVBoxLayout(this);
- layout->setMargin(0);
- layout->addWidget(m_editor);
-}
-
-void EnvironmentItemsWidget::setEnvironmentItems(const QList<EnvironmentItem> &items)
-{
- QList<EnvironmentItem> sortedItems = items;
- EnvironmentItem::sort(&sortedItems);
- const QStringList list = EnvironmentItem::toStringList(sortedItems);
- m_editor->document()->setPlainText(list.join(QLatin1Char('\n')));
-}
-
-QList<EnvironmentItem> EnvironmentItemsWidget::environmentItems() const
-{
- const QStringList list = m_editor->document()->toPlainText().split(QLatin1String("\n"));
- QList<EnvironmentItem> items = EnvironmentItem::fromStringList(list);
- return cleanUp(items);
-}
-
-void EnvironmentItemsWidget::setPlaceholderText(const QString &text)
-{
- m_editor->setPlaceholderText(text);
-}
-
-class EnvironmentDialogPrivate
-{
-public:
- EnvironmentItemsWidget *m_editor;
-};
-
-} // namespace Internal
-
-EnvironmentDialog::EnvironmentDialog(QWidget *parent) :
- QDialog(parent), d(new Internal::EnvironmentDialogPrivate)
-{
- setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
- resize(640, 480);
- d->m_editor = new Internal::EnvironmentItemsWidget(this);
- auto box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
- connect(box, &QDialogButtonBox::accepted, this, &QDialog::accept);
- connect(box, &QDialogButtonBox::rejected, this, &QDialog::reject);
-
- auto helpLabel = new QLabel(this);
- helpLabel->setText(tr("Enter one environment variable per line.\n"
- "To set or change a variable, use VARIABLE=VALUE.\n"
- "Existing variables can be referenced in a VALUE with ${OTHER}.\n"
- "To clear a variable, put its name on a line with nothing else on it."));
-
- auto layout = new QVBoxLayout(this);
- layout->addWidget(d->m_editor);
- layout->addWidget(helpLabel);
-
- layout->addWidget(box);
-
- setWindowTitle(tr("Edit Environment"));
-}
-
-EnvironmentDialog::~EnvironmentDialog()
-{
- delete d;
-}
-
-void EnvironmentDialog::setEnvironmentItems(const QList<EnvironmentItem> &items)
-{
- d->m_editor->setEnvironmentItems(items);
-}
-
-QList<EnvironmentItem> EnvironmentDialog::environmentItems() const
+Utils::optional<EnvironmentItems> EnvironmentDialog::getEnvironmentItems(
+ QWidget *parent, const EnvironmentItems &initial, const QString &placeholderText, Polisher polisher)
{
- return d->m_editor->environmentItems();
-}
-
-void EnvironmentDialog::setPlaceholderText(const QString &text)
-{
- d->m_editor->setPlaceholderText(text);
-}
-
-QList<EnvironmentItem> EnvironmentDialog::getEnvironmentItems(bool *ok,
- QWidget *parent,
- const QList<EnvironmentItem> &initial,
- const QString &placeholderText,
- Polisher polisher)
-{
- EnvironmentDialog dlg(parent);
- if (polisher)
- polisher(&dlg);
- dlg.setEnvironmentItems(initial);
- dlg.setPlaceholderText(placeholderText);
- bool result = dlg.exec() == QDialog::Accepted;
- if (ok)
- *ok = result;
- if (result)
- return dlg.environmentItems();
- return QList<EnvironmentItem>();
+ return getNameValueItems(
+ parent,
+ initial,
+ placeholderText,
+ polisher,
+ tr("Edit Environment"),
+ tr("Enter one environment variable per line.\n"
+ "To set or change a variable, use VARIABLE=VALUE.\n"
+ "Existing variables can be referenced in a VALUE with ${OTHER}.\n"
+ "To clear a variable, put its name on a line with nothing else on it."));
}
} // namespace Utils
-
-#include "environmentdialog.moc"
diff --git a/src/libs/utils/environmentdialog.h b/src/libs/utils/environmentdialog.h
index be8218508b..6dbb38e191 100644
--- a/src/libs/utils/environmentdialog.h
+++ b/src/libs/utils/environmentdialog.h
@@ -25,36 +25,20 @@
#pragma once
-#include "utils_global.h"
#include "environment.h"
-
-#include <QDialog>
+#include "namevaluesdialog.h"
+#include <thread>
namespace Utils {
-namespace Internal { class EnvironmentDialogPrivate; }
-
-class QTCREATOR_UTILS_EXPORT EnvironmentDialog : public QDialog
+class QTCREATOR_UTILS_EXPORT EnvironmentDialog : public NameValuesDialog
{
Q_OBJECT
public:
- explicit EnvironmentDialog(QWidget *parent = nullptr);
- ~EnvironmentDialog() override;
-
- void setEnvironmentItems(const QList<EnvironmentItem> &items);
- QList<EnvironmentItem> environmentItems() const;
-
- void setPlaceholderText(const QString &text);
-
- using Polisher = std::function<void(QWidget*)>;
- static QList<EnvironmentItem> getEnvironmentItems(bool *ok,
- QWidget *parent = nullptr,
- const QList<EnvironmentItem> &initial = QList<EnvironmentItem>(),
- const QString &placeholderText = QString(),
- Polisher polish = Polisher());
-
-private:
- Internal::EnvironmentDialogPrivate *d;
+ static Utils::optional<EnvironmentItems> getEnvironmentItems(QWidget *parent = nullptr,
+ const EnvironmentItems &initial = {},
+ const QString &placeholderText = {},
+ Polisher polish = {});
};
} // namespace Utils
diff --git a/src/libs/utils/environmentfwd.h b/src/libs/utils/environmentfwd.h
new file mode 100644
index 0000000000..04a57418b5
--- /dev/null
+++ b/src/libs/utils/environmentfwd.h
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+template<typename Type>
+class QList;
+class QTreeView;
+QT_END_NAMESPACE
+
+namespace Utils {
+class NameValueDictionary;
+class NameValueItem;
+using NameValueItems = QVector<NameValueItem>;
+
+class Environment;
+using EnvironmentItem = NameValueItem;
+using EnvironmentItems = NameValueItems;
+
+class PreprocessorMacroDictionary;
+using PreprocessorMacroItem = NameValueItem;
+using PreprocessorMacroItems = NameValueItems;
+
+class NameValueModel;
+class EnvironmentModel;
+} // namespace Utils
diff --git a/src/libs/utils/environmentmodel.cpp b/src/libs/utils/environmentmodel.cpp
index 045a6d2b65..c7efcce930 100644
--- a/src/libs/utils/environmentmodel.cpp
+++ b/src/libs/utils/environmentmodel.cpp
@@ -33,360 +33,12 @@
#include <QFont>
namespace Utils {
-namespace Internal {
-
-class EnvironmentModelPrivate
-{
-public:
- void updateResultEnvironment()
- {
- m_resultEnvironment = m_baseEnvironment;
- m_resultEnvironment.modify(m_items);
- // Add removed variables again and mark them as "<UNSET>" so
- // that the user can actually see those removals:
- foreach (const EnvironmentItem &item, m_items) {
- if (item.operation == EnvironmentItem::Unset)
- m_resultEnvironment.set(item.name, EnvironmentModel::tr("<UNSET>"));
- }
- }
-
- int findInChanges(const QString &name) const
- {
- for (int i=0; i<m_items.size(); ++i)
- if (m_items.at(i).name == name)
- return i;
- return -1;
- }
-
- int findInResultInsertPosition(const QString &name) const
- {
- Environment::const_iterator it;
- int i = 0;
- for (it = m_resultEnvironment.constBegin(); it != m_resultEnvironment.constEnd(); ++it, ++i)
- if (m_resultEnvironment.key(it) > name)
- return i;
- return m_resultEnvironment.size();
- }
-
- int findInResult(const QString &name) const
- {
- Environment::const_iterator it;
- int i = 0;
- for (it = m_resultEnvironment.constBegin(); it != m_resultEnvironment.constEnd(); ++it, ++i)
- if (m_resultEnvironment.key(it) == name)
- return i;
- return -1;
- }
-
- Environment m_baseEnvironment;
- Environment m_resultEnvironment;
- QList<EnvironmentItem> m_items;
-};
-
-} // namespace Internal
-
-EnvironmentModel::EnvironmentModel(QObject *parent) :
- QAbstractTableModel(parent),
- d(new Internal::EnvironmentModelPrivate)
-{ }
-
-EnvironmentModel::~EnvironmentModel()
-{
- delete d;
-}
-
-QString EnvironmentModel::indexToVariable(const QModelIndex &index) const
+const Environment &EnvironmentModel::baseEnvironment() const
{
- return d->m_resultEnvironment.key(d->m_resultEnvironment.constBegin() + index.row());
+ return static_cast<const Environment &>(baseNameValueDictionary());
}
-
void EnvironmentModel::setBaseEnvironment(const Environment &env)
{
- if (d->m_baseEnvironment == env)
- return;
- beginResetModel();
- d->m_baseEnvironment = env;
- d->updateResultEnvironment();
- endResetModel();
-}
-
-int EnvironmentModel::rowCount(const QModelIndex &parent) const
-{
- if (parent.isValid())
- return 0;
-
- return d->m_resultEnvironment.size();
-}
-int EnvironmentModel::columnCount(const QModelIndex &parent) const
-{
- if (parent.isValid())
- return 0;
-
- return 2;
-}
-
-bool EnvironmentModel::changes(const QString &name) const
-{
- return d->findInChanges(name) >= 0;
-}
-
-Environment EnvironmentModel::baseEnvironment() const
-{
- return d->m_baseEnvironment;
-}
-
-QVariant EnvironmentModel::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid())
- return QVariant();
-
- if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole) {
- if (index.column() == 0) {
- return d->m_resultEnvironment.key(d->m_resultEnvironment.constBegin() + index.row());
- } else if (index.column() == 1) {
- // Do not return "<UNSET>" when editing a previously unset variable:
- if (role == Qt::EditRole) {
- int pos = d->findInChanges(indexToVariable(index));
- if (pos >= 0)
- return d->m_items.at(pos).value;
- }
- QString value = d->m_resultEnvironment.value(d->m_resultEnvironment.constBegin() + index.row());
- if (role == Qt::ToolTipRole && value.length() > 80) {
- // Use html to enable text wrapping
- value = value.toHtmlEscaped();
- value.prepend(QLatin1String("<html><body>"));
- value.append(QLatin1String("</body></html>"));
- }
- return value;
- }
- }
- if (role == Qt::FontRole) {
- // check whether this environment variable exists in d->m_items
- if (changes(d->m_resultEnvironment.key(d->m_resultEnvironment.constBegin() + index.row()))) {
- QFont f;
- f.setBold(true);
- return QVariant(f);
- }
- return QFont();
- }
- return QVariant();
-}
-
-Qt::ItemFlags EnvironmentModel::flags(const QModelIndex &index) const
-{
- Q_UNUSED(index)
- return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
-}
-
-QVariant EnvironmentModel::headerData(int section, Qt::Orientation orientation, int role) const
-{
- if (orientation == Qt::Vertical || role != Qt::DisplayRole)
- return QVariant();
- return section == 0 ? tr("Variable") : tr("Value");
+ setBaseNameValueDictionary(env);
}
-
-/// *****************
-/// Utility functions
-/// *****************
-QModelIndex EnvironmentModel::variableToIndex(const QString &name) const
-{
- int row = d->findInResult(name);
- if (row == -1)
- return QModelIndex();
- return index(row, 0);
-}
-
-bool EnvironmentModel::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- if (!index.isValid() || role != Qt::EditRole)
- return false;
-
- // ignore changes to already set values:
- if (data(index, role) == value)
- return true;
-
- const QString oldName = data(this->index(index.row(), 0, QModelIndex())).toString();
- const QString oldValue = data(this->index(index.row(), 1, QModelIndex()), Qt::EditRole).toString();
- int changesPos = d->findInChanges(oldName);
-
- if (index.column() == 0) {
- //fail if a variable with the same name already exists
- const QString &newName = HostOsInfo::isWindowsHost()
- ? value.toString().toUpper() : value.toString();
- if (newName.isEmpty() || newName.contains('='))
- return false;
- // Does the new name exist already?
- if (d->m_resultEnvironment.hasKey(newName) || newName.isEmpty())
- return false;
-
- EnvironmentItem newVariable(newName, oldValue);
-
- if (changesPos != -1)
- resetVariable(oldName); // restore the original base variable again
-
- QModelIndex newIndex = addVariable(newVariable); // add the new variable
- emit focusIndex(newIndex.sibling(newIndex.row(), 1)); // hint to focus on the value
- return true;
- } else if (index.column() == 1) {
- // We are changing an existing value:
- const QString stringValue = value.toString();
- if (changesPos != -1) {
- // We have already changed this value
- if (d->m_baseEnvironment.hasKey(oldName) && stringValue == d->m_baseEnvironment.value(oldName)) {
- // ... and now went back to the base value
- d->m_items.removeAt(changesPos);
- } else {
- // ... and changed it again
- d->m_items[changesPos].value = stringValue;
- d->m_items[changesPos].operation = EnvironmentItem::Set;
- }
- } else {
- // Add a new change item:
- d->m_items.append(EnvironmentItem(oldName, stringValue));
- }
- d->updateResultEnvironment();
- emit dataChanged(index, index);
- emit userChangesChanged();
- return true;
- }
- return false;
-}
-
-QModelIndex EnvironmentModel::addVariable()
-{
- //: Name when inserting a new variable
- return addVariable(EnvironmentItem(tr("<VARIABLE>"),
- //: Value when inserting a new variable
- tr("<VALUE>")));
-}
-
-QModelIndex EnvironmentModel::addVariable(const EnvironmentItem &item)
-{
-
- // Return existing index if the name is already in the result set:
- int pos = d->findInResult(item.name);
- if (pos >= 0 && pos < d->m_resultEnvironment.size())
- return index(pos, 0, QModelIndex());
-
- int insertPos = d->findInResultInsertPosition(item.name);
- int changePos = d->findInChanges(item.name);
- if (d->m_baseEnvironment.hasKey(item.name)) {
- // We previously unset this!
- Q_ASSERT(changePos >= 0);
- // Do not insert a line here as we listed the variable as <UNSET> before!
- Q_ASSERT(d->m_items.at(changePos).name == item.name);
- Q_ASSERT(d->m_items.at(changePos).operation == EnvironmentItem::Unset);
- Q_ASSERT(d->m_items.at(changePos).value.isEmpty());
- d->m_items[changePos] = item;
- emit dataChanged(index(insertPos, 0, QModelIndex()), index(insertPos, 1, QModelIndex()));
- } else {
- // We add something that is not in the base environment
- // Insert a new line!
- beginInsertRows(QModelIndex(), insertPos, insertPos);
- Q_ASSERT(changePos < 0);
- d->m_items.append(item);
- d->updateResultEnvironment();
- endInsertRows();
- }
- emit userChangesChanged();
- return index(insertPos, 0, QModelIndex());
-}
-
-void EnvironmentModel::resetVariable(const QString &name)
-{
- int rowInChanges = d->findInChanges(name);
- if (rowInChanges < 0)
- return;
-
- int rowInResult = d->findInResult(name);
- if (rowInResult < 0)
- return;
-
- if (d->m_baseEnvironment.hasKey(name)) {
- d->m_items.removeAt(rowInChanges);
- d->updateResultEnvironment();
- emit dataChanged(index(rowInResult, 0, QModelIndex()), index(rowInResult, 1, QModelIndex()));
- emit userChangesChanged();
- } else {
- // Remove the line completely!
- beginRemoveRows(QModelIndex(), rowInResult, rowInResult);
- d->m_items.removeAt(rowInChanges);
- d->updateResultEnvironment();
- endRemoveRows();
- emit userChangesChanged();
- }
-}
-
-void EnvironmentModel::unsetVariable(const QString &name)
-{
- // This does not change the number of rows as we will display a <UNSET>
- // in place of the original variable!
- int row = d->findInResult(name);
- if (row < 0)
- return;
-
- // look in d->m_items for the variable
- int pos = d->findInChanges(name);
- if (pos != -1) {
- d->m_items[pos].operation = EnvironmentItem::Unset;
- d->m_items[pos].value.clear();
- d->updateResultEnvironment();
- emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
- emit userChangesChanged();
- return;
- }
- d->m_items.append(EnvironmentItem(name, QString(), EnvironmentItem::Unset));
- d->updateResultEnvironment();
- emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
- emit userChangesChanged();
-}
-
-bool EnvironmentModel::canUnset(const QString &name)
-{
- int pos = d->findInChanges(name);
- if (pos != -1)
- return d->m_items.at(pos).operation == EnvironmentItem::Unset;
- else
- return false;
-}
-
-bool EnvironmentModel::canReset(const QString &name)
-{
- return d->m_baseEnvironment.hasKey(name);
-}
-
-QList<EnvironmentItem> EnvironmentModel::userChanges() const
-{
- return d->m_items;
-}
-
-void EnvironmentModel::setUserChanges(const QList<EnvironmentItem> &list)
-{
- QList<EnvironmentItem> filtered = Utils::filtered(list, [](const EnvironmentItem &i) {
- return i.name != "export " && !i.name.contains('=');
- });
- // We assume nobody is reordering the items here.
- if (filtered == d->m_items)
- return;
- beginResetModel();
- d->m_items = filtered;
- for (EnvironmentItem &item : d->m_items) {
- QString &name = item.name;
- name = name.trimmed();
- if (name.startsWith("export "))
- name = name.mid(7).trimmed();
- if (d->m_baseEnvironment.osType() == OsTypeWindows) {
- // Environment variable names are case-insensitive under windows, but we still
- // want to preserve the case of pre-existing variables.
- auto it = d->m_baseEnvironment.constFind(name);
- if (it != d->m_baseEnvironment.constEnd())
- name = d->m_baseEnvironment.key(it);
- }
- }
-
- d->updateResultEnvironment();
- endResetModel();
- emit userChangesChanged();
-}
-
} // namespace Utils
diff --git a/src/libs/utils/environmentmodel.h b/src/libs/utils/environmentmodel.h
index db20224651..ac5f017448 100644
--- a/src/libs/utils/environmentmodel.h
+++ b/src/libs/utils/environmentmodel.h
@@ -25,55 +25,17 @@
#pragma once
-#include "utils_global.h"
-
-#include <QAbstractTableModel>
+#include "namevaluemodel.h"
namespace Utils {
-class Environment;
-class EnvironmentItem;
-
-namespace Internal { class EnvironmentModelPrivate; }
-class QTCREATOR_UTILS_EXPORT EnvironmentModel : public QAbstractTableModel
+class QTCREATOR_UTILS_EXPORT EnvironmentModel : public NameValueModel
{
Q_OBJECT
public:
- explicit EnvironmentModel(QObject *parent = nullptr);
- ~EnvironmentModel() override;
-
- int rowCount(const QModelIndex &parent) const override;
- int columnCount(const QModelIndex &parent) const override;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
- Qt::ItemFlags flags(const QModelIndex &index) const override;
- QVariant headerData(int section, Qt::Orientation orientation,
- int role = Qt::DisplayRole) const override;
-
- QModelIndex addVariable();
- QModelIndex addVariable(const EnvironmentItem &item);
- void resetVariable(const QString &name);
- void unsetVariable(const QString &name);
- bool canUnset(const QString &name);
- bool canReset(const QString &name);
- QString indexToVariable(const QModelIndex &index) const;
- QModelIndex variableToIndex(const QString &name) const;
- bool changes(const QString &key) const;
- Environment baseEnvironment() const;
+ const Environment &baseEnvironment() const;
void setBaseEnvironment(const Environment &env);
- QList<EnvironmentItem> userChanges() const;
- void setUserChanges(const QList<EnvironmentItem> &list);
-
-signals:
- void userChangesChanged();
- /// Hint to the view where it should make sense to focus on next
- // This is a hack since there is no way for a model to suggest
- // the next interesting place to focus on to the view.
- void focusIndex(const QModelIndex &index);
-
-private:
- Internal::EnvironmentModelPrivate *d;
};
} // namespace Utils
diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp
index acf9c6190d..22fb05240b 100644
--- a/src/libs/utils/fileutils.cpp
+++ b/src/libs/utils/fileutils.cpp
@@ -74,6 +74,23 @@ namespace Utils {
*/
+CommandLine::CommandLine(const FilePath &executable)
+ : m_executable(executable)
+{}
+
+CommandLine::CommandLine(const FilePath &exe, const QStringList &args, MetaCharMode metaCharMode)
+ : m_executable(exe)
+ , m_metaCharMode(metaCharMode)
+{
+ addArgs(args);
+}
+
+CommandLine::CommandLine(const FilePath &exe, const QString &args, RawType)
+ : m_executable(exe)
+{
+ addArgs(args, Raw);
+}
+
void CommandLine::addArg(const QString &arg, OsType osType)
{
QtcProcess::addArg(&m_arguments, arg, osType);
@@ -85,7 +102,7 @@ void CommandLine::addArgs(const QStringList &inArgs, OsType osType)
addArg(arg, osType);
}
-void CommandLine::addArgs(const QString &inArgs)
+void CommandLine::addArgs(const QString &inArgs, RawType)
{
QtcProcess::addArgs(&m_arguments, inArgs);
}
@@ -95,6 +112,11 @@ QString CommandLine::toUserOutput() const
return m_executable.toUserOutput() + ' ' + m_arguments;
}
+QStringList CommandLine::splitArguments(OsType osType) const
+{
+ return QtcProcess::splitArgs(m_arguments, osType);
+}
+
/*! \class Utils::FileUtils
\brief The FileUtils class contains file and directory related convenience
diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h
index ec018b9c3b..4b5ce6dc48 100644
--- a/src/libs/utils/fileutils.h
+++ b/src/libs/utils/fileutils.h
@@ -131,24 +131,32 @@ using FileNameList = FilePathList;
class QTCREATOR_UTILS_EXPORT CommandLine
{
public:
- CommandLine() {}
+ enum RawType { Raw };
+ enum class MetaCharMode { Abort, Ignore };
- CommandLine(const FilePath &executable, const QString &arguments)
- : m_executable(executable), m_arguments(arguments)
- {}
+ CommandLine() {}
+ explicit CommandLine(const FilePath &executable);
+ CommandLine(const FilePath &exe,
+ const QStringList &args,
+ MetaCharMode metaCharMode = MetaCharMode::Abort);
+ CommandLine(const FilePath &exe, const QString &unparsedArgs, RawType);
void addArg(const QString &arg, OsType osType = HostOsInfo::hostOs());
void addArgs(const QStringList &inArgs, OsType osType = HostOsInfo::hostOs());
- void addArgs(const QString &inArgs);
+
+ void addArgs(const QString &inArgs, RawType);
QString toUserOutput() const;
FilePath executable() const { return m_executable; }
QString arguments() const { return m_arguments; }
+ MetaCharMode metaCharMode() const { return m_metaCharMode; }
+ QStringList splitArguments(OsType osType = HostOsInfo::hostOs()) const;
private:
FilePath m_executable;
QString m_arguments;
+ MetaCharMode m_metaCharMode;
};
class QTCREATOR_UTILS_EXPORT FileUtils {
diff --git a/src/libs/utils/listmodel.h b/src/libs/utils/listmodel.h
index 5f4de811e6..4a28041b66 100644
--- a/src/libs/utils/listmodel.h
+++ b/src/libs/utils/listmodel.h
@@ -67,9 +67,9 @@ public:
void clear() { rootItem()->removeChildren(); }
- using const_iterator = typename QVector<TreeItem *>::const_iterator;
- const_iterator begin() const { return rootItem()->begin(); }
- const_iterator end() const { return rootItem()->end(); }
+ using const_iterator = typename QVector<ChildType *>::const_iterator;
+ const_iterator begin() const { return const_iterator(rootItem()->begin()); }
+ const_iterator end() const { return const_iterator(rootItem()->end()); }
};
diff --git a/src/libs/utils/namevaluedictionary.cpp b/src/libs/utils/namevaluedictionary.cpp
new file mode 100644
index 0000000000..30e3c18448
--- /dev/null
+++ b/src/libs/utils/namevaluedictionary.cpp
@@ -0,0 +1,282 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "algorithm.h"
+#include "namevaluedictionary.h"
+#include "qtcassert.h"
+
+#include <QDir>
+
+namespace Utils {
+
+namespace {
+NameValueMap::iterator findKey(NameValueMap &input, Utils::OsType osType, const QString &key)
+{
+ const Qt::CaseSensitivity casing = (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive
+ : Qt::CaseSensitive;
+ for (auto it = input.begin(); it != input.end(); ++it) {
+ if (key.compare(it.key(), casing) == 0)
+ return it;
+ }
+ return input.end();
+}
+
+NameValueMap::const_iterator findKey(const NameValueMap &input, Utils::OsType osType, const QString &key)
+{
+ const Qt::CaseSensitivity casing = (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive
+ : Qt::CaseSensitive;
+ for (auto it = input.constBegin(); it != input.constEnd(); ++it) {
+ if (key.compare(it.key(), casing) == 0)
+ return it;
+ }
+ return input.constEnd();
+}
+} // namespace
+
+NameValueDictionary::NameValueDictionary(const QStringList &env, OsType osType)
+ : m_osType(osType)
+{
+ for (const QString &s : env) {
+ int i = s.indexOf('=', 1);
+ if (i >= 0) {
+ const QString key = s.left(i);
+ if (!key.contains('=')) {
+ const QString value = s.mid(i + 1);
+ set(key, value);
+ }
+ }
+ }
+}
+
+NameValueDictionary::NameValueDictionary(const NameValuePairs &nameValues)
+{
+ for (const auto &nameValue : nameValues)
+ set(nameValue.first, nameValue.second);
+}
+
+QStringList NameValueDictionary::toStringList() const
+{
+ QStringList result;
+ for (auto it = m_values.constBegin(); it != m_values.constEnd(); ++it)
+ result.append(it.key() + '=' + it.value());
+ return result;
+}
+
+void NameValueDictionary::set(const QString &key, const QString &value)
+{
+ QTC_ASSERT(!key.contains('='), return );
+ auto it = findKey(m_values, m_osType, key);
+ if (it == m_values.end())
+ m_values.insert(key, value);
+ else
+ it.value() = value;
+}
+
+void NameValueDictionary::unset(const QString &key)
+{
+ QTC_ASSERT(!key.contains('='), return );
+ auto it = findKey(m_values, m_osType, key);
+ if (it != m_values.end())
+ m_values.erase(it);
+}
+
+void NameValueDictionary::clear()
+{
+ m_values.clear();
+}
+
+QString NameValueDictionary::value(const QString &key) const
+{
+ const auto it = findKey(m_values, m_osType, key);
+ return it != m_values.end() ? it.value() : QString();
+}
+
+NameValueDictionary::const_iterator NameValueDictionary::constFind(const QString &name) const
+{
+ return findKey(m_values, m_osType, name);
+}
+
+int NameValueDictionary::size() const
+{
+ return m_values.size();
+}
+
+void NameValueDictionary::modify(const NameValueItems &items)
+{
+ NameValueDictionary resultKeyValueDictionary = *this;
+ for (const NameValueItem &item : items)
+ item.apply(&resultKeyValueDictionary);
+ *this = resultKeyValueDictionary;
+}
+
+enum : char {
+#ifdef Q_OS_WIN
+ pathSepC = ';'
+#else
+ pathSepC = ':'
+#endif
+};
+
+NameValueItems NameValueDictionary::diff(const NameValueDictionary &other, bool checkAppendPrepend) const
+{
+ NameValueMap::const_iterator thisIt = constBegin();
+ NameValueMap::const_iterator otherIt = other.constBegin();
+
+ NameValueItems result;
+ while (thisIt != constEnd() || otherIt != other.constEnd()) {
+ if (thisIt == constEnd()) {
+ result.append(NameValueItem(otherIt.key(), otherIt.value()));
+ ++otherIt;
+ } else if (otherIt == other.constEnd()) {
+ result.append(NameValueItem(thisIt.key(), QString(), NameValueItem::Unset));
+ ++thisIt;
+ } else if (thisIt.key() < otherIt.key()) {
+ result.append(NameValueItem(thisIt.key(), QString(), NameValueItem::Unset));
+ ++thisIt;
+ } else if (thisIt.key() > otherIt.key()) {
+ result.append(NameValueItem(otherIt.key(), otherIt.value()));
+ ++otherIt;
+ } else {
+ const QString &oldValue = thisIt.value();
+ const QString &newValue = otherIt.value();
+ if (oldValue != newValue) {
+ if (checkAppendPrepend && newValue.startsWith(oldValue)) {
+ QString appended = newValue.right(newValue.size() - oldValue.size());
+ if (appended.startsWith(QLatin1Char(pathSepC)))
+ appended.remove(0, 1);
+ result.append(NameValueItem(otherIt.key(), appended, NameValueItem::Append));
+ } else if (checkAppendPrepend && newValue.endsWith(oldValue)) {
+ QString prepended = newValue.left(newValue.size() - oldValue.size());
+ if (prepended.endsWith(QLatin1Char(pathSepC)))
+ prepended.chop(1);
+ result.append(NameValueItem(otherIt.key(), prepended, NameValueItem::Prepend));
+ } else {
+ result.append(NameValueItem(otherIt.key(), newValue));
+ }
+ }
+ ++otherIt;
+ ++thisIt;
+ }
+ }
+ return result;
+}
+
+bool NameValueDictionary::hasKey(const QString &key) const
+{
+ return m_values.contains(key);
+}
+
+OsType NameValueDictionary::osType() const
+{
+ return m_osType;
+}
+
+QString NameValueDictionary::userName() const
+{
+ return value(QString::fromLatin1(m_osType == OsTypeWindows ? "USERNAME" : "USER"));
+}
+
+/** Expand environment variables in a string.
+ *
+ * KeyValueDictionary variables are accepted in the following forms:
+ * $SOMEVAR, ${SOMEVAR} on Unix and %SOMEVAR% on Windows.
+ * No escapes and quoting are supported.
+ * If a variable is not found, it is not substituted.
+ */
+QString NameValueDictionary::expandVariables(const QString &input) const
+{
+ QString result = input;
+
+ if (m_osType == OsTypeWindows) {
+ for (int vStart = -1, i = 0; i < result.length();) {
+ if (result.at(i++) == '%') {
+ if (vStart > 0) {
+ const_iterator it = findKey(m_values, m_osType, result.mid(vStart, i - vStart - 1));
+ if (it != m_values.constEnd()) {
+ result.replace(vStart - 1, i - vStart + 1, *it);
+ i = vStart - 1 + it->length();
+ vStart = -1;
+ } else {
+ vStart = i;
+ }
+ } else {
+ vStart = i;
+ }
+ }
+ }
+ } else {
+ enum { BASE, OPTIONALVARIABLEBRACE, VARIABLE, BRACEDVARIABLE } state = BASE;
+ int vStart = -1;
+
+ for (int i = 0; i < result.length();) {
+ QChar c = result.at(i++);
+ if (state == BASE) {
+ if (c == '$')
+ state = OPTIONALVARIABLEBRACE;
+ } else if (state == OPTIONALVARIABLEBRACE) {
+ if (c == '{') {
+ state = BRACEDVARIABLE;
+ vStart = i;
+ } else if (c.isLetterOrNumber() || c == '_') {
+ state = VARIABLE;
+ vStart = i - 1;
+ } else {
+ state = BASE;
+ }
+ } else if (state == BRACEDVARIABLE) {
+ if (c == '}') {
+ const_iterator it = m_values.constFind(result.mid(vStart, i - 1 - vStart));
+ if (it != constEnd()) {
+ result.replace(vStart - 2, i - vStart + 2, *it);
+ i = vStart - 2 + it->length();
+ }
+ state = BASE;
+ }
+ } else if (state == VARIABLE) {
+ if (!c.isLetterOrNumber() && c != '_') {
+ const_iterator it = m_values.constFind(result.mid(vStart, i - vStart - 1));
+ if (it != constEnd()) {
+ result.replace(vStart - 1, i - vStart, *it);
+ i = vStart - 1 + it->length();
+ }
+ state = BASE;
+ }
+ }
+ }
+ if (state == VARIABLE) {
+ const_iterator it = m_values.constFind(result.mid(vStart));
+ if (it != constEnd())
+ result.replace(vStart - 1, result.length() - vStart + 1, *it);
+ }
+ }
+ return result;
+}
+
+QStringList NameValueDictionary::expandVariables(const QStringList &variables) const
+{
+ return Utils::transform(variables, [this](const QString &i) { return expandVariables(i); });
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/namevaluedictionary.h b/src/libs/utils/namevaluedictionary.h
new file mode 100644
index 0000000000..112d6b094c
--- /dev/null
+++ b/src/libs/utils/namevaluedictionary.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "fileutils.h"
+#include "hostosinfo.h"
+#include "namevalueitem.h"
+
+namespace Utils {
+
+using NameValuePair = std::pair<QString, QString>;
+using NameValuePairs = QVector<NameValuePair>;
+using NameValueMap = QMap<QString, QString>;
+
+class QTCREATOR_UTILS_EXPORT NameValueDictionary
+{
+public:
+ using const_iterator = NameValueMap::const_iterator;
+
+ explicit NameValueDictionary(OsType osType = HostOsInfo::hostOs())
+ : m_osType(osType)
+ {}
+ explicit NameValueDictionary(const QStringList &env, OsType osType = HostOsInfo::hostOs());
+ explicit NameValueDictionary(const NameValuePairs &nameValues);
+
+ QStringList toStringList() const;
+ QString value(const QString &key) const;
+ void set(const QString &key, const QString &value);
+ void unset(const QString &key);
+ void modify(const NameValueItems &items);
+ /// Return the KeyValueDictionary changes necessary to modify this into the other environment.
+ NameValueItems diff(const NameValueDictionary &other, bool checkAppendPrepend = false) const;
+ bool hasKey(const QString &key) const;
+ OsType osType() const;
+
+ QString userName() const;
+
+ void clear();
+ int size() const;
+
+ QString key(NameValueDictionary::const_iterator it) const { return it.key(); }
+
+ QString value(NameValueDictionary::const_iterator it) const { return it.value(); }
+
+ NameValueDictionary::const_iterator constBegin() const { return m_values.constBegin(); }
+
+ NameValueDictionary::const_iterator constEnd() const { return m_values.constEnd(); }
+
+ NameValueDictionary::const_iterator constFind(const QString &name) const;
+
+ QString expandVariables(const QString &input) const;
+ QStringList expandVariables(const QStringList &input) const;
+
+ friend bool operator!=(const NameValueDictionary &first, const NameValueDictionary &second)
+ {
+ return !(first == second);
+ }
+
+ friend bool operator==(const NameValueDictionary &first, const NameValueDictionary &second)
+ {
+ return first.m_osType == second.m_osType && first.m_values == second.m_values;
+ }
+
+protected:
+ NameValueMap m_values;
+ OsType m_osType;
+};
+
+} // namespace Utils
diff --git a/src/libs/utils/namevalueitem.cpp b/src/libs/utils/namevalueitem.cpp
new file mode 100644
index 0000000000..6be972201c
--- /dev/null
+++ b/src/libs/utils/namevalueitem.cpp
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "namevalueitem.h"
+#include "algorithm.h"
+#include "namevaluedictionary.h"
+#include "qtcassert.h"
+
+#include <QDebug>
+
+namespace Utils {
+
+void NameValueItem::sort(NameValueItems *list)
+{
+ Utils::sort(*list, &NameValueItem::name);
+}
+
+NameValueItems NameValueItem::fromStringList(const QStringList &list)
+{
+ NameValueItems result;
+ for (const QString &string : list) {
+ int pos = string.indexOf('=', 1);
+ if (pos == -1)
+ result.append(NameValueItem(string, QString(), NameValueItem::Unset));
+ else
+ result.append(NameValueItem(string.left(pos), string.mid(pos + 1)));
+ }
+ return result;
+}
+
+QStringList NameValueItem::toStringList(const NameValueItems &list)
+{
+ return Utils::transform<QStringList>(list, [](const NameValueItem &item) {
+ if (item.operation == NameValueItem::Unset)
+ return QString(item.name);
+ return QString(item.name + '=' + item.value);
+ });
+}
+
+NameValueItems NameValueItem::itemsFromVariantList(const QVariantList &list)
+{
+ return Utils::transform<NameValueItems>(list, [](const QVariant &item) {
+ return itemFromVariantList(item.toList());
+ });
+}
+
+QVariantList NameValueItem::toVariantList(const NameValueItems &list)
+{
+ return Utils::transform<QVariantList>(list, [](const NameValueItem &item) {
+ return QVariant(toVariantList(item));
+ });
+}
+
+NameValueItem NameValueItem::itemFromVariantList(const QVariantList &list)
+{
+ QTC_ASSERT(list.size() == 3, return NameValueItem("", ""));
+ QString key = list.value(0).toString();
+ Operation operation = Operation(list.value(1).toInt());
+ QString value = list.value(2).toString();
+ return NameValueItem(key, value, operation);
+}
+
+QVariantList NameValueItem::toVariantList(const NameValueItem &item)
+{
+ return QVariantList() << item.name << item.operation << item.value;
+}
+
+static QString expand(const NameValueDictionary *dictionary, QString value)
+{
+ int replaceCount = 0;
+ for (int i = 0; i < value.size(); ++i) {
+ if (value.at(i) == '$') {
+ if ((i + 1) < value.size()) {
+ const QChar &c = value.at(i + 1);
+ int end = -1;
+ if (c == '(')
+ end = value.indexOf(')', i);
+ else if (c == '{')
+ end = value.indexOf('}', i);
+ if (end != -1) {
+ const QString &key = value.mid(i + 2, end - i - 2);
+ NameValueDictionary::const_iterator it = dictionary->constFind(key);
+ if (it != dictionary->constEnd())
+ value.replace(i, end - i + 1, it.value());
+ ++replaceCount;
+ QTC_ASSERT(replaceCount < 100, break);
+ }
+ }
+ }
+ }
+ return value;
+}
+
+enum : char {
+#ifdef Q_OS_WIN
+ pathSepC = ';'
+#else
+ pathSepC = ':'
+#endif
+};
+
+void NameValueItem::apply(NameValueDictionary *dictionary, Operation op) const
+{
+ switch (op) {
+ case Set:
+ dictionary->set(name, expand(dictionary, value));
+ break;
+ case Unset:
+ dictionary->unset(name);
+ break;
+ case Prepend: {
+ const NameValueDictionary::const_iterator it = dictionary->constFind(name);
+ if (it != dictionary->constEnd()) {
+ QString v = it.value();
+ const QChar pathSep{QLatin1Char(pathSepC)};
+ int sepCount = 0;
+ if (v.startsWith(pathSep))
+ ++sepCount;
+ if (value.endsWith(pathSep))
+ ++sepCount;
+ if (sepCount == 2)
+ v.remove(0, 1);
+ else if (sepCount == 0)
+ v.prepend(pathSep);
+ v.prepend(expand(dictionary, value));
+ dictionary->set(name, v);
+ } else {
+ apply(dictionary, Set);
+ }
+ } break;
+ case Append: {
+ const NameValueDictionary::const_iterator it = dictionary->constFind(name);
+ if (it != dictionary->constEnd()) {
+ QString v = it.value();
+ const QChar pathSep{QLatin1Char(pathSepC)};
+ int sepCount = 0;
+ if (v.endsWith(pathSep))
+ ++sepCount;
+ if (value.startsWith(pathSep))
+ ++sepCount;
+ if (sepCount == 2)
+ v.chop(1);
+ else if (sepCount == 0)
+ v.append(pathSep);
+ v.append(expand(dictionary, value));
+ dictionary->set(name, v);
+ } else {
+ apply(dictionary, Set);
+ }
+ } break;
+ }
+}
+
+QDebug operator<<(QDebug debug, const NameValueItem &i)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "KeyValueItem(";
+ switch (i.operation) {
+ case NameValueItem::Set:
+ debug << "set \"" << i.name << "\" to \"" << i.value << '"';
+ break;
+ case NameValueItem::Unset:
+ debug << "unset \"" << i.name << '"';
+ break;
+ case NameValueItem::Prepend:
+ debug << "prepend to \"" << i.name << "\":\"" << i.value << '"';
+ break;
+ case NameValueItem::Append:
+ debug << "append to \"" << i.name << "\":\"" << i.value << '"';
+ break;
+ }
+ debug << ')';
+ return debug;
+}
+} // namespace Utils
diff --git a/src/libs/utils/namevalueitem.h b/src/libs/utils/namevalueitem.h
new file mode 100644
index 0000000000..3ed4cc3cb3
--- /dev/null
+++ b/src/libs/utils/namevalueitem.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "environmentfwd.h"
+#include "utils_global.h"
+
+#include <QStringList>
+#include <QVariantList>
+#include <QVector>
+
+namespace Utils {
+
+class QTCREATOR_UTILS_EXPORT NameValueItem
+{
+public:
+ enum Operation : char { Set, Unset, Prepend, Append };
+ NameValueItem() = default;
+ NameValueItem(const QString &key, const QString &value, Operation operation = Set)
+ : name(key)
+ , value(value)
+ , operation(operation)
+ {}
+
+ void apply(NameValueDictionary *dictionary) const { apply(dictionary, operation); }
+
+ static void sort(NameValueItems *list);
+ static NameValueItems fromStringList(const QStringList &list);
+ static QStringList toStringList(const NameValueItems &list);
+ static NameValueItems itemsFromVariantList(const QVariantList &list);
+ static QVariantList toVariantList(const NameValueItems &list);
+ static NameValueItem itemFromVariantList(const QVariantList &list);
+ static QVariantList toVariantList(const NameValueItem &item);
+
+ friend bool operator==(const NameValueItem &first, const NameValueItem &second)
+ {
+ return first.operation == second.operation && first.name == second.name
+ && first.value == second.value;
+ }
+
+ friend bool operator!=(const NameValueItem &first, const NameValueItem &second)
+ {
+ return !(first == second);
+ }
+
+public:
+ QString name;
+ QString value;
+ Operation operation = Unset;
+
+private:
+ void apply(NameValueDictionary *dictionary, Operation op) const;
+};
+
+QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug debug, const NameValueItem &i);
+
+} // namespace Utils
diff --git a/src/libs/utils/namevaluemodel.cpp b/src/libs/utils/namevaluemodel.cpp
new file mode 100644
index 0000000000..2a310d6b61
--- /dev/null
+++ b/src/libs/utils/namevaluemodel.cpp
@@ -0,0 +1,397 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "namevaluemodel.h"
+
+#include <utils/algorithm.h>
+#include <utils/hostosinfo.h>
+#include <utils/namevaluedictionary.h>
+
+#include <QFont>
+#include <QString>
+
+namespace Utils {
+namespace Internal {
+
+class NameValueModelPrivate
+{
+public:
+ void updateResultNameValueDictionary()
+ {
+ m_resultNameValueDictionary = m_baseNameValueDictionary;
+ m_resultNameValueDictionary.modify(m_items);
+ // Add removed variables again and mark them as "<UNSET>" so
+ // that the user can actually see those removals:
+ foreach (const NameValueItem &item, m_items) {
+ if (item.operation == NameValueItem::Unset)
+ m_resultNameValueDictionary.set(item.name, NameValueModel::tr("<UNSET>"));
+ }
+ }
+
+ int findInChanges(const QString &name) const
+ {
+ for (int i = 0; i < m_items.size(); ++i)
+ if (m_items.at(i).name == name)
+ return i;
+ return -1;
+ }
+
+ int findInResultInsertPosition(const QString &name) const
+ {
+ NameValueDictionary::const_iterator it;
+ int i = 0;
+ for (it = m_resultNameValueDictionary.constBegin();
+ it != m_resultNameValueDictionary.constEnd();
+ ++it, ++i)
+ if (m_resultNameValueDictionary.key(it) > name)
+ return i;
+ return m_resultNameValueDictionary.size();
+ }
+
+ int findInResult(const QString &name) const
+ {
+ NameValueDictionary::const_iterator it;
+ int i = 0;
+ for (it = m_resultNameValueDictionary.constBegin();
+ it != m_resultNameValueDictionary.constEnd();
+ ++it, ++i)
+ if (m_resultNameValueDictionary.key(it) == name)
+ return i;
+ return -1;
+ }
+
+ NameValueDictionary m_baseNameValueDictionary;
+ NameValueDictionary m_resultNameValueDictionary;
+ NameValueItems m_items;
+};
+
+} // namespace Internal
+
+NameValueModel::NameValueModel(QObject *parent)
+ : QAbstractTableModel(parent)
+ , d(std::make_unique<Internal::NameValueModelPrivate>())
+{}
+
+NameValueModel::~NameValueModel() = default;
+
+QString NameValueModel::indexToVariable(const QModelIndex &index) const
+{
+ return d->m_resultNameValueDictionary.key(d->m_resultNameValueDictionary.constBegin()
+ + index.row());
+}
+
+void NameValueModel::setBaseNameValueDictionary(const NameValueDictionary &dictionary)
+{
+ if (d->m_baseNameValueDictionary == dictionary)
+ return;
+ beginResetModel();
+ d->m_baseNameValueDictionary = dictionary;
+ d->updateResultNameValueDictionary();
+ endResetModel();
+}
+
+int NameValueModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return d->m_resultNameValueDictionary.size();
+}
+int NameValueModel::columnCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return 2;
+}
+
+bool NameValueModel::changes(const QString &name) const
+{
+ return d->findInChanges(name) >= 0;
+}
+
+const NameValueDictionary &NameValueModel::baseNameValueDictionary() const
+{
+ return d->m_baseNameValueDictionary;
+}
+
+QVariant NameValueModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole) {
+ if (index.column() == 0) {
+ return d->m_resultNameValueDictionary.key(d->m_resultNameValueDictionary.constBegin()
+ + index.row());
+ } else if (index.column() == 1) {
+ // Do not return "<UNSET>" when editing a previously unset variable:
+ if (role == Qt::EditRole) {
+ int pos = d->findInChanges(indexToVariable(index));
+ if (pos >= 0)
+ return d->m_items.at(pos).value;
+ }
+ QString value = d->m_resultNameValueDictionary.value(
+ d->m_resultNameValueDictionary.constBegin() + index.row());
+ if (role == Qt::ToolTipRole && value.length() > 80) {
+ // Use html to enable text wrapping
+ value = value.toHtmlEscaped();
+ value.prepend(QLatin1String("<html><body>"));
+ value.append(QLatin1String("</body></html>"));
+ }
+ return value;
+ }
+ }
+ if (role == Qt::FontRole) {
+ // check whether this name value item variable exists in d->m_items
+ if (changes(d->m_resultNameValueDictionary.key(d->m_resultNameValueDictionary.constBegin()
+ + index.row()))) {
+ QFont f;
+ f.setBold(true);
+ return QVariant(f);
+ }
+ return QFont();
+ }
+ return QVariant();
+}
+
+Qt::ItemFlags NameValueModel::flags(const QModelIndex &index) const
+{
+ Q_UNUSED(index)
+ return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
+}
+
+QVariant NameValueModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Vertical || role != Qt::DisplayRole)
+ return QVariant();
+ return section == 0 ? tr("Variable") : tr("Value");
+}
+
+/// *****************
+/// Utility functions
+/// *****************
+QModelIndex NameValueModel::variableToIndex(const QString &name) const
+{
+ int row = d->findInResult(name);
+ if (row == -1)
+ return QModelIndex();
+ return index(row, 0);
+}
+
+bool NameValueModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!index.isValid() || role != Qt::EditRole)
+ return false;
+
+ // ignore changes to already set values:
+ if (data(index, role) == value)
+ return true;
+
+ const QString oldName = data(this->index(index.row(), 0, QModelIndex())).toString();
+ const QString oldValue = data(this->index(index.row(), 1, QModelIndex()), Qt::EditRole).toString();
+ int changesPos = d->findInChanges(oldName);
+
+ if (index.column() == 0) {
+ //fail if a variable with the same name already exists
+ const QString &newName = HostOsInfo::isWindowsHost() ? value.toString().toUpper()
+ : value.toString();
+ if (newName.isEmpty() || newName.contains('='))
+ return false;
+ // Does the new name exist already?
+ if (d->m_resultNameValueDictionary.hasKey(newName) || newName.isEmpty())
+ return false;
+
+ NameValueItem newVariable(newName, oldValue);
+
+ if (changesPos != -1)
+ resetVariable(oldName); // restore the original base variable again
+
+ QModelIndex newIndex = addVariable(newVariable); // add the new variable
+ emit focusIndex(newIndex.sibling(newIndex.row(), 1)); // hint to focus on the value
+ return true;
+ } else if (index.column() == 1) {
+ // We are changing an existing value:
+ const QString stringValue = value.toString();
+ if (changesPos != -1) {
+ // We have already changed this value
+ if (d->m_baseNameValueDictionary.hasKey(oldName)
+ && stringValue == d->m_baseNameValueDictionary.value(oldName)) {
+ // ... and now went back to the base value
+ d->m_items.removeAt(changesPos);
+ } else {
+ // ... and changed it again
+ d->m_items[changesPos].value = stringValue;
+ d->m_items[changesPos].operation = NameValueItem::Set;
+ }
+ } else {
+ // Add a new change item:
+ d->m_items.append(NameValueItem(oldName, stringValue));
+ }
+ d->updateResultNameValueDictionary();
+ emit dataChanged(index, index);
+ emit userChangesChanged();
+ return true;
+ }
+ return false;
+}
+
+QModelIndex NameValueModel::addVariable()
+{
+ //: Name when inserting a new variable
+ return addVariable(NameValueItem(tr("<VARIABLE>"),
+ //: Value when inserting a new variable
+ tr("<VALUE>")));
+}
+
+QModelIndex NameValueModel::addVariable(const NameValueItem &item)
+{
+ // Return existing index if the name is already in the result set:
+ int pos = d->findInResult(item.name);
+ if (pos >= 0 && pos < d->m_resultNameValueDictionary.size())
+ return index(pos, 0, QModelIndex());
+
+ int insertPos = d->findInResultInsertPosition(item.name);
+ int changePos = d->findInChanges(item.name);
+ if (d->m_baseNameValueDictionary.hasKey(item.name)) {
+ // We previously unset this!
+ Q_ASSERT(changePos >= 0);
+ // Do not insert a line here as we listed the variable as <UNSET> before!
+ Q_ASSERT(d->m_items.at(changePos).name == item.name);
+ Q_ASSERT(d->m_items.at(changePos).operation == NameValueItem::Unset);
+ Q_ASSERT(d->m_items.at(changePos).value.isEmpty());
+ d->m_items[changePos] = item;
+ emit dataChanged(index(insertPos, 0, QModelIndex()), index(insertPos, 1, QModelIndex()));
+ } else {
+ // We add something that is not in the base dictionary
+ // Insert a new line!
+ beginInsertRows(QModelIndex(), insertPos, insertPos);
+ Q_ASSERT(changePos < 0);
+ d->m_items.append(item);
+ d->updateResultNameValueDictionary();
+ endInsertRows();
+ }
+ emit userChangesChanged();
+ return index(insertPos, 0, QModelIndex());
+}
+
+void NameValueModel::resetVariable(const QString &name)
+{
+ int rowInChanges = d->findInChanges(name);
+ if (rowInChanges < 0)
+ return;
+
+ int rowInResult = d->findInResult(name);
+ if (rowInResult < 0)
+ return;
+
+ if (d->m_baseNameValueDictionary.hasKey(name)) {
+ d->m_items.removeAt(rowInChanges);
+ d->updateResultNameValueDictionary();
+ emit dataChanged(index(rowInResult, 0, QModelIndex()), index(rowInResult, 1, QModelIndex()));
+ emit userChangesChanged();
+ } else {
+ // Remove the line completely!
+ beginRemoveRows(QModelIndex(), rowInResult, rowInResult);
+ d->m_items.removeAt(rowInChanges);
+ d->updateResultNameValueDictionary();
+ endRemoveRows();
+ emit userChangesChanged();
+ }
+}
+
+void NameValueModel::unsetVariable(const QString &name)
+{
+ // This does not change the number of rows as we will display a <UNSET>
+ // in place of the original variable!
+ int row = d->findInResult(name);
+ if (row < 0)
+ return;
+
+ // look in d->m_items for the variable
+ int pos = d->findInChanges(name);
+ if (pos != -1) {
+ d->m_items[pos].operation = NameValueItem::Unset;
+ d->m_items[pos].value.clear();
+ d->updateResultNameValueDictionary();
+ emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
+ emit userChangesChanged();
+ return;
+ }
+ d->m_items.append(NameValueItem(name, QString(), NameValueItem::Unset));
+ d->updateResultNameValueDictionary();
+ emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
+ emit userChangesChanged();
+}
+
+bool NameValueModel::canUnset(const QString &name)
+{
+ int pos = d->findInChanges(name);
+ if (pos != -1)
+ return d->m_items.at(pos).operation == NameValueItem::Unset;
+ else
+ return false;
+}
+
+bool NameValueModel::canReset(const QString &name)
+{
+ return d->m_baseNameValueDictionary.hasKey(name);
+}
+
+NameValueItems NameValueModel::userChanges() const
+{
+ return d->m_items;
+}
+
+void NameValueModel::setUserChanges(const NameValueItems &items)
+{
+ NameValueItems filtered = Utils::filtered(items, [](const NameValueItem &i) {
+ return i.name != "export " && !i.name.contains('=');
+ });
+ // We assume nobody is reordering the items here.
+ if (filtered == d->m_items)
+ return;
+ beginResetModel();
+ d->m_items = filtered;
+ for (NameValueItem &item : d->m_items) {
+ QString &name = item.name;
+ name = name.trimmed();
+ if (name.startsWith("export "))
+ name = name.mid(7).trimmed();
+ if (d->m_baseNameValueDictionary.osType() == OsTypeWindows) {
+ // NameValueDictionary variable names are case-insensitive under windows, but we still
+ // want to preserve the case of pre-existing variables.
+ auto it = d->m_baseNameValueDictionary.constFind(name);
+ if (it != d->m_baseNameValueDictionary.constEnd())
+ name = d->m_baseNameValueDictionary.key(it);
+ }
+ }
+
+ d->updateResultNameValueDictionary();
+ endResetModel();
+ emit userChangesChanged();
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/namevaluemodel.h b/src/libs/utils/namevaluemodel.h
new file mode 100644
index 0000000000..96811fec68
--- /dev/null
+++ b/src/libs/utils/namevaluemodel.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "environmentfwd.h"
+#include "utils_global.h"
+
+#include <QAbstractTableModel>
+
+#include <memory>
+
+namespace Utils {
+
+namespace Internal {
+class NameValueModelPrivate;
+}
+
+class QTCREATOR_UTILS_EXPORT NameValueModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ explicit NameValueModel(QObject *parent = nullptr);
+ ~NameValueModel() override;
+
+ int rowCount(const QModelIndex &parent) const override;
+ int columnCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ QVariant headerData(int section,
+ Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const override;
+
+ QModelIndex addVariable();
+ QModelIndex addVariable(const NameValueItem &item);
+ void resetVariable(const QString &name);
+ void unsetVariable(const QString &name);
+ bool canUnset(const QString &name);
+ bool canReset(const QString &name);
+ QString indexToVariable(const QModelIndex &index) const;
+ QModelIndex variableToIndex(const QString &name) const;
+ bool changes(const QString &key) const;
+ const NameValueDictionary &baseNameValueDictionary() const;
+ void setBaseNameValueDictionary(const NameValueDictionary &dictionary);
+ NameValueItems userChanges() const;
+ void setUserChanges(const NameValueItems &items);
+
+signals:
+ void userChangesChanged();
+ /// Hint to the view where it should make sense to focus on next
+ // This is a hack since there is no way for a model to suggest
+ // the next interesting place to focus on to the view.
+ void focusIndex(const QModelIndex &index);
+
+private:
+ std::unique_ptr<Internal::NameValueModelPrivate> d;
+};
+
+} // namespace Utils
diff --git a/src/libs/utils/namevaluesdialog.cpp b/src/libs/utils/namevaluesdialog.cpp
new file mode 100644
index 0000000000..ed71d8db7f
--- /dev/null
+++ b/src/libs/utils/namevaluesdialog.cpp
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "namevaluesdialog.h"
+
+#include <utils/environment.h>
+#include <utils/hostosinfo.h>
+
+#include <QDialogButtonBox>
+#include <QLabel>
+#include <QPlainTextEdit>
+#include <QVBoxLayout>
+
+namespace Utils {
+
+namespace Internal {
+
+static EnvironmentItems cleanUp(const EnvironmentItems &items)
+{
+ EnvironmentItems uniqueItems;
+ QSet<QString> uniqueSet;
+ for (int i = items.count() - 1; i >= 0; i--) {
+ EnvironmentItem item = items.at(i);
+ if (HostOsInfo::isWindowsHost())
+ item.name = item.name.toUpper();
+ const QString &itemName = item.name;
+ QString emptyName = itemName;
+ emptyName.remove(QLatin1Char(' '));
+ if (!emptyName.isEmpty() && !uniqueSet.contains(itemName)) {
+ uniqueItems.prepend(item);
+ uniqueSet.insert(itemName);
+ }
+ }
+ return uniqueItems;
+}
+
+NameValueItemsWidget::NameValueItemsWidget(QWidget *parent)
+ : QWidget(parent)
+{
+ m_editor = new QPlainTextEdit(this);
+ auto layout = new QVBoxLayout(this);
+ layout->setMargin(0);
+ layout->addWidget(m_editor);
+}
+
+void NameValueItemsWidget::setEnvironmentItems(const EnvironmentItems &items)
+{
+ EnvironmentItems sortedItems = items;
+ EnvironmentItem::sort(&sortedItems);
+ const QStringList list = EnvironmentItem::toStringList(sortedItems);
+ m_editor->document()->setPlainText(list.join(QLatin1Char('\n')));
+}
+
+EnvironmentItems NameValueItemsWidget::environmentItems() const
+{
+ const QStringList list = m_editor->document()->toPlainText().split(QLatin1String("\n"));
+ EnvironmentItems items = EnvironmentItem::fromStringList(list);
+ return cleanUp(items);
+}
+
+void NameValueItemsWidget::setPlaceholderText(const QString &text)
+{
+ m_editor->setPlaceholderText(text);
+}
+} // namespace Internal
+
+NameValuesDialog::NameValuesDialog(const QString &windowTitle, const QString &helpText, QWidget *parent)
+ : QDialog(parent)
+{
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ resize(640, 480);
+ m_editor = new Internal::NameValueItemsWidget(this);
+ auto box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
+ Qt::Horizontal,
+ this);
+ connect(box, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(box, &QDialogButtonBox::rejected, this, &QDialog::reject);
+
+ auto helpLabel = new QLabel(this);
+ helpLabel->setText(helpText);
+
+ auto layout = new QVBoxLayout(this);
+ layout->addWidget(m_editor);
+ layout->addWidget(helpLabel);
+
+ layout->addWidget(box);
+
+ setWindowTitle(windowTitle);
+}
+
+void NameValuesDialog::setNameValueItems(const EnvironmentItems &items)
+{
+ m_editor->setEnvironmentItems(items);
+}
+
+EnvironmentItems NameValuesDialog::nameValueItems() const
+{
+ return m_editor->environmentItems();
+}
+
+void NameValuesDialog::setPlaceholderText(const QString &text)
+{
+ m_editor->setPlaceholderText(text);
+}
+
+Utils::optional<NameValueItems> NameValuesDialog::getNameValueItems(QWidget *parent,
+ const NameValueItems &initial,
+ const QString &placeholderText,
+ Polisher polisher,
+ const QString &windowTitle,
+ const QString &helpText)
+{
+ NameValuesDialog dialog(windowTitle, helpText, parent);
+ if (polisher)
+ polisher(&dialog);
+ dialog.setNameValueItems(initial);
+ dialog.setPlaceholderText(placeholderText);
+ bool result = dialog.exec() == QDialog::Accepted;
+ if (result)
+ return dialog.nameValueItems();
+
+ return {};
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/namevaluesdialog.h b/src/libs/utils/namevaluesdialog.h
new file mode 100644
index 0000000000..8170a99a1d
--- /dev/null
+++ b/src/libs/utils/namevaluesdialog.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "environmentfwd.h"
+#include "optional.h"
+#include "utils_global.h"
+
+#include <QDialog>
+
+#include <functional>
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+class QPlainTextEdit;
+QT_END_NAMESPACE
+
+namespace Utils {
+
+namespace Internal {
+class NameValueItemsWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit NameValueItemsWidget(QWidget *parent = nullptr);
+
+ void setEnvironmentItems(const EnvironmentItems &items);
+ EnvironmentItems environmentItems() const;
+
+ void setPlaceholderText(const QString &text);
+
+private:
+ QPlainTextEdit *m_editor;
+};
+} // namespace Internal
+
+class QTCREATOR_UTILS_EXPORT NameValuesDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ void setNameValueItems(const NameValueItems &items);
+ NameValueItems nameValueItems() const;
+
+ void setPlaceholderText(const QString &text);
+
+ using Polisher = std::function<void(QWidget *)>;
+ static Utils::optional<NameValueItems> getNameValueItems(QWidget *parent = nullptr,
+ const NameValueItems &initial = {},
+ const QString &placeholderText = {},
+ Polisher polish = {},
+ const QString &windowTitle = {},
+ const QString &helpText = {});
+
+protected:
+ explicit NameValuesDialog(const QString &windowTitle,
+ const QString &helpText,
+ QWidget *parent = {});
+
+private:
+ Internal::NameValueItemsWidget *m_editor;
+};
+
+} // namespace Utils
diff --git a/src/libs/utils/namevaluevalidator.cpp b/src/libs/utils/namevaluevalidator.cpp
new file mode 100644
index 0000000000..580a476e01
--- /dev/null
+++ b/src/libs/utils/namevaluevalidator.cpp
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "namevaluevalidator.h"
+#include "namevaluemodel.h"
+#include "tooltip/tooltip.h"
+
+#include <QTreeView>
+
+namespace Utils {
+
+NameValueValidator::NameValueValidator(QWidget *parent,
+ Utils::NameValueModel *model,
+ QTreeView *view,
+ const QModelIndex &index,
+ const QString &toolTipText)
+ : QValidator(parent)
+ , m_toolTipText(toolTipText)
+ , m_model(model)
+ , m_view(view)
+ , m_index(index)
+{
+ m_hideTipTimer.setInterval(2000);
+ m_hideTipTimer.setSingleShot(true);
+ connect(&m_hideTipTimer, &QTimer::timeout, this, []() { Utils::ToolTip::hide(); });
+}
+
+QValidator::State NameValueValidator::validate(QString &in, int &pos) const
+{
+ Q_UNUSED(pos)
+ QModelIndex idx = m_model->variableToIndex(in);
+ if (idx.isValid() && idx != m_index)
+ return QValidator::Intermediate;
+ Utils::ToolTip::hide();
+ m_hideTipTimer.stop();
+ return QValidator::Acceptable;
+}
+
+void NameValueValidator::fixup(QString &input) const
+{
+ Q_UNUSED(input)
+
+ QPoint pos = m_view->mapToGlobal(m_view->visualRect(m_index).topLeft());
+ pos -= Utils::ToolTip::offsetFromPosition();
+ Utils::ToolTip::show(pos, m_toolTipText);
+ m_hideTipTimer.start();
+ // do nothing
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/namevaluevalidator.h b/src/libs/utils/namevaluevalidator.h
new file mode 100644
index 0000000000..e1a8ae13a0
--- /dev/null
+++ b/src/libs/utils/namevaluevalidator.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "environmentfwd.h"
+#include "utils_global.h"
+
+#include <QModelIndex>
+#include <QTimer>
+#include <QValidator>
+
+namespace Utils {
+
+class QTCREATOR_UTILS_EXPORT NameValueValidator : public QValidator
+{
+ Q_OBJECT
+public:
+ NameValueValidator(QWidget *parent,
+ Utils::NameValueModel *model,
+ QTreeView *view,
+ const QModelIndex &index,
+ const QString &toolTipText);
+
+ QValidator::State validate(QString &in, int &pos) const override;
+
+ void fixup(QString &input) const override;
+
+private:
+ const QString &m_toolTipText;
+ Utils::NameValueModel *m_model;
+ QTreeView *m_view;
+ QModelIndex m_index;
+ mutable QTimer m_hideTipTimer;
+};
+} // namespace Utils
diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp
index f08391d6a6..a114aba310 100644
--- a/src/libs/utils/pathchooser.cpp
+++ b/src/libs/utils/pathchooser.cpp
@@ -84,7 +84,7 @@ public:
QStringList arguments() const { return m_arguments; }
void setArguments(const QStringList &arguments) { m_arguments = arguments; }
- static QString toolVersion(const QString &binary, const QStringList &arguments);
+ static QString toolVersion(const CommandLine &cmd);
private:
// Extension point for concatenating existing tooltips.
@@ -108,7 +108,8 @@ bool BinaryVersionToolTipEventFilter::eventFilter(QObject *o, QEvent *e)
const QString binary = le->text();
if (!binary.isEmpty()) {
- const QString version = BinaryVersionToolTipEventFilter::toolVersion(QDir::cleanPath(binary), m_arguments);
+ const QString version = BinaryVersionToolTipEventFilter::toolVersion(
+ CommandLine(FilePath::fromString(QDir::cleanPath(binary)), m_arguments));
if (!version.isEmpty()) {
// Concatenate tooltips.
QString tooltip = "<html><head/><body>";
@@ -127,13 +128,13 @@ bool BinaryVersionToolTipEventFilter::eventFilter(QObject *o, QEvent *e)
return false;
}
-QString BinaryVersionToolTipEventFilter::toolVersion(const QString &binary, const QStringList &arguments)
+QString BinaryVersionToolTipEventFilter::toolVersion(const CommandLine &cmd)
{
- if (binary.isEmpty())
+ if (cmd.executable().isEmpty())
return QString();
SynchronousProcess proc;
proc.setTimeoutS(1);
- SynchronousProcessResponse response = proc.runBlocking(binary, arguments);
+ SynchronousProcessResponse response = proc.runBlocking(cmd);
if (response.result != SynchronousProcessResponse::Finished)
return QString();
return response.allOutput();
@@ -677,7 +678,7 @@ FancyLineEdit *PathChooser::lineEdit() const
QString PathChooser::toolVersion(const QString &binary, const QStringList &arguments)
{
- return BinaryVersionToolTipEventFilter::toolVersion(binary, arguments);
+ return BinaryVersionToolTipEventFilter::toolVersion({FilePath::fromString(binary), arguments});
}
void PathChooser::installLineEditVersionToolTip(QLineEdit *le, const QStringList &arguments)
diff --git a/src/libs/utils/settingsaccessor.cpp b/src/libs/utils/settingsaccessor.cpp
index f6d90df7e0..881652f5ea 100644
--- a/src/libs/utils/settingsaccessor.cpp
+++ b/src/libs/utils/settingsaccessor.cpp
@@ -277,7 +277,10 @@ BackingUpSettingsAccessor::readData(const FilePath &path, QWidget *parent) const
QApplication::translate("Utils::SettingsAccessor",
"<p>No valid settings file could be found.</p>"
"<p>All settings files found in directory \"%1\" "
- "were unsuitable for the current version of %2.</p>")
+ "were unsuitable for the current version of %2, "
+ "for instance because they were written by an incompatible "
+ "version of %2, or because a different settings path "
+ "was used.</p>")
.arg(path.toUserOutput()).arg(applicationDisplayName), Issue::Type::ERROR);
i.buttons.insert(QMessageBox::Ok, DiscardAndContinue);
result.issue = i;
diff --git a/src/libs/utils/shellcommand.cpp b/src/libs/utils/shellcommand.cpp
index 5a84c92f15..99b0b0ace6 100644
--- a/src/libs/utils/shellcommand.cpp
+++ b/src/libs/utils/shellcommand.cpp
@@ -68,12 +68,11 @@ class ShellCommandPrivate
{
public:
struct Job {
- explicit Job(const QString &wd, const FilePath &b, const QStringList &a, int t,
+ explicit Job(const QString &wd, const CommandLine &command, int t,
const ExitCodeInterpreter &interpreter);
QString workingDirectory;
- FilePath binary;
- QStringList arguments;
+ CommandLine command;
ExitCodeInterpreter exitCodeInterpreter;
int timeoutS;
};
@@ -113,11 +112,10 @@ ShellCommandPrivate::~ShellCommandPrivate()
delete m_progressParser;
}
-ShellCommandPrivate::Job::Job(const QString &wd, const FilePath &b, const QStringList &a,
+ShellCommandPrivate::Job::Job(const QString &wd, const CommandLine &command,
int t, const ExitCodeInterpreter &interpreter) :
workingDirectory(wd),
- binary(b),
- arguments(a),
+ command(command),
exitCodeInterpreter(interpreter),
timeoutS(t)
{
@@ -146,14 +144,14 @@ QString ShellCommand::displayName() const
return d->m_displayName;
if (!d->m_jobs.isEmpty()) {
const Internal::ShellCommandPrivate::Job &job = d->m_jobs.at(0);
- QString result = job.binary.toFileInfo().baseName();
+ QString result = job.command.executable().toFileInfo().baseName();
if (!result.isEmpty())
result[0] = result.at(0).toTitleCase();
else
result = tr("UNKNOWN");
- if (!job.arguments.isEmpty())
- result += QLatin1Char(' ') + job.arguments.at(0);
+ if (!job.command.arguments().isEmpty())
+ result += ' ' + job.command.arguments().at(0);
return result;
}
@@ -195,17 +193,17 @@ void ShellCommand::addFlags(unsigned f)
d->m_flags |= f;
}
-void ShellCommand::addJob(const FilePath &binary, const QStringList &arguments,
+void ShellCommand::addJob(const CommandLine &command,
const QString &workingDirectory, const ExitCodeInterpreter &interpreter)
{
- addJob(binary, arguments, defaultTimeoutS(), workingDirectory, interpreter);
+ addJob(command, defaultTimeoutS(), workingDirectory, interpreter);
}
-void ShellCommand::addJob(const FilePath &binary, const QStringList &arguments, int timeoutS,
+void ShellCommand::addJob(const CommandLine &command, int timeoutS,
const QString &workingDirectory, const ExitCodeInterpreter &interpreter)
{
- d->m_jobs.push_back(Internal::ShellCommandPrivate::Job(workDirectory(workingDirectory), binary,
- arguments, timeoutS, interpreter));
+ d->m_jobs.push_back(Internal::ShellCommandPrivate::Job(workDirectory(workingDirectory), command,
+ timeoutS, interpreter));
}
void ShellCommand::execute()
@@ -287,7 +285,7 @@ void ShellCommand::run(QFutureInterface<void> &future)
for (int j = 0; j < count; j++) {
const Internal::ShellCommandPrivate::Job &job = d->m_jobs.at(j);
SynchronousProcessResponse resp
- = runCommand(job.binary, job.arguments, job.timeoutS, job.workingDirectory,
+ = runCommand(job.command, job.timeoutS, job.workingDirectory,
job.exitCodeInterpreter);
stdOut += resp.stdOut();
stdErr += resp.stdErr();
@@ -319,8 +317,7 @@ void ShellCommand::run(QFutureInterface<void> &future)
this->deleteLater();
}
-SynchronousProcessResponse ShellCommand::runCommand(const FilePath &binary,
- const QStringList &arguments, int timeoutS,
+SynchronousProcessResponse ShellCommand::runCommand(const CommandLine &command, int timeoutS,
const QString &workingDirectory,
const ExitCodeInterpreter &interpreter)
{
@@ -328,7 +325,7 @@ SynchronousProcessResponse ShellCommand::runCommand(const FilePath &binary,
const QString dir = workDirectory(workingDirectory);
- if (binary.isEmpty()) {
+ if (command.executable().isEmpty()) {
response.result = SynchronousProcessResponse::StartFailed;
return response;
}
@@ -336,31 +333,30 @@ SynchronousProcessResponse ShellCommand::runCommand(const FilePath &binary,
QSharedPointer<OutputProxy> proxy(d->m_proxyFactory());
if (!(d->m_flags & SuppressCommandLogging))
- emit proxy->appendCommand(dir, binary, arguments);
+ emit proxy->appendCommand(dir, command);
if ((d->m_flags & FullySynchronously)
|| (!(d->m_flags & NoFullySync)
&& QThread::currentThread() == QCoreApplication::instance()->thread())) {
- response = runFullySynchronous(binary, arguments, proxy, timeoutS, dir, interpreter);
+ response = runFullySynchronous(command, proxy, timeoutS, dir, interpreter);
} else {
- response = runSynchronous(binary, arguments, proxy, timeoutS, dir, interpreter);
+ response = runSynchronous(command, proxy, timeoutS, dir, interpreter);
}
if (!d->m_aborted) {
// Success/Fail message in appropriate window?
if (response.result == SynchronousProcessResponse::Finished) {
if (d->m_flags & ShowSuccessMessage)
- emit proxy->appendMessage(response.exitMessage(binary.toUserOutput(), timeoutS));
+ emit proxy->appendMessage(response.exitMessage(command.toUserOutput(), timeoutS));
} else if (!(d->m_flags & SuppressFailMessage)) {
- emit proxy->appendError(response.exitMessage(binary.toUserOutput(), timeoutS));
+ emit proxy->appendError(response.exitMessage(command.toUserOutput(), timeoutS));
}
}
return response;
}
-SynchronousProcessResponse ShellCommand::runFullySynchronous(const FilePath &binary,
- const QStringList &arguments,
+SynchronousProcessResponse ShellCommand::runFullySynchronous(const CommandLine &cmd,
QSharedPointer<OutputProxy> proxy,
int timeoutS,
const QString &workingDirectory,
@@ -380,7 +376,7 @@ SynchronousProcessResponse ShellCommand::runFullySynchronous(const FilePath &bin
process.setTimeoutS(timeoutS);
process.setExitCodeInterpreter(interpreter);
- SynchronousProcessResponse resp = process.runBlocking(binary.toString(), arguments);
+ SynchronousProcessResponse resp = process.runBlocking(cmd);
if (!d->m_aborted) {
const QString stdErr = resp.stdErr();
@@ -399,8 +395,7 @@ SynchronousProcessResponse ShellCommand::runFullySynchronous(const FilePath &bin
return resp;
}
-SynchronousProcessResponse ShellCommand::runSynchronous(const FilePath &binary,
- const QStringList &arguments,
+SynchronousProcessResponse ShellCommand::runSynchronous(const CommandLine &cmd,
QSharedPointer<OutputProxy> proxy,
int timeoutS,
const QString &workingDirectory,
@@ -460,7 +455,7 @@ SynchronousProcessResponse ShellCommand::runSynchronous(const FilePath &binary,
process.setTimeoutS(timeoutS);
process.setExitCodeInterpreter(interpreter);
- return process.run(binary.toString(), arguments);
+ return process.run(cmd);
}
const QVariant &ShellCommand::cookie() const
diff --git a/src/libs/utils/shellcommand.h b/src/libs/utils/shellcommand.h
index f0c427ef53..a54e8fb401 100644
--- a/src/libs/utils/shellcommand.h
+++ b/src/libs/utils/shellcommand.h
@@ -46,7 +46,7 @@ QT_END_NAMESPACE
namespace Utils {
-class FilePath;
+class CommandLine;
namespace Internal { class ShellCommandPrivate; }
class QTCREATOR_UTILS_EXPORT ProgressParser
@@ -79,8 +79,7 @@ signals:
void append(const QString &text);
void appendSilently(const QString &text);
void appendError(const QString &text);
- void appendCommand(const QString &workingDirectory, const Utils::FilePath &binary,
- const QStringList &args);
+ void appendCommand(const QString &workingDirectory, const Utils::CommandLine &command);
void appendMessage(const QString &text);
};
@@ -112,10 +111,12 @@ public:
QString displayName() const;
void setDisplayName(const QString &name);
- void addJob(const FilePath &binary, const QStringList &arguments,
- const QString &workingDirectory = QString(), const ExitCodeInterpreter &interpreter = defaultExitCodeInterpreter);
- void addJob(const FilePath &binary, const QStringList &arguments, int timeoutS,
- const QString &workingDirectory = QString(), const ExitCodeInterpreter &interpreter = defaultExitCodeInterpreter);
+ void addJob(const CommandLine &command,
+ const QString &workingDirectory = QString(),
+ const ExitCodeInterpreter &interpreter = defaultExitCodeInterpreter);
+ void addJob(const CommandLine &command, int timeoutS,
+ const QString &workingDirectory = QString(),
+ const ExitCodeInterpreter &interpreter = defaultExitCodeInterpreter);
void execute(); // Execute tasks asynchronously!
void abort();
bool lastExecutionSuccess() const;
@@ -145,7 +146,7 @@ public:
// This is called once per job in a thread.
// When called from the UI thread it will execute fully synchronously, so no signals will
// be triggered!
- virtual SynchronousProcessResponse runCommand(const FilePath &binary, const QStringList &arguments,
+ virtual SynchronousProcessResponse runCommand(const CommandLine &command,
int timeoutS,
const QString &workingDirectory = QString(),
const ExitCodeInterpreter &interpreter = defaultExitCodeInterpreter);
@@ -171,12 +172,12 @@ private:
void run(QFutureInterface<void> &future);
// Run without a event loop in fully blocking mode. No signals will be delivered.
- SynchronousProcessResponse runFullySynchronous(const FilePath &binary, const QStringList &arguments,
+ SynchronousProcessResponse runFullySynchronous(const CommandLine &cmd,
QSharedPointer<OutputProxy> proxy,
int timeoutS, const QString &workingDirectory,
const ExitCodeInterpreter &interpreter = defaultExitCodeInterpreter);
// Run with an event loop. Signals will be delivered.
- SynchronousProcessResponse runSynchronous(const FilePath &binary, const QStringList &arguments,
+ SynchronousProcessResponse runSynchronous(const CommandLine &cmd,
QSharedPointer<OutputProxy> proxy,
int timeoutS, const QString &workingDirectory,
const ExitCodeInterpreter &interpreter = defaultExitCodeInterpreter);
diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp
index 161cf44e74..f73e9d8144 100644
--- a/src/libs/utils/stringutils.cpp
+++ b/src/libs/utils/stringutils.cpp
@@ -136,6 +136,7 @@ bool AbstractMacroExpander::expandNestedMacros(const QString &str, int *pos, QSt
QString *currArg = &varName;
QChar prev;
QChar c;
+ QChar replacementChar;
bool replaceAll = false;
int i = *pos;
@@ -192,13 +193,14 @@ bool AbstractMacroExpander::expandNestedMacros(const QString &str, int *pos, QSt
} else if (currArg == &varName && c == '-' && prev == ':' && validateVarName(varName)) {
varName.chop(1);
currArg = &defaultValue;
- } else if (currArg == &varName && c == '/' && validateVarName(varName)) {
+ } else if (currArg == &varName && (c == '/' || c == '#') && validateVarName(varName)) {
+ replacementChar = c;
currArg = &pattern;
- if (i < strLen && str.at(i) == '/') {
+ if (i < strLen && str.at(i) == replacementChar) {
++i;
replaceAll = true;
}
- } else if (currArg == &pattern && c == '/') {
+ } else if (currArg == &pattern && c == replacementChar) {
currArg = &replace;
} else {
*currArg += c;
diff --git a/src/libs/utils/synchronousprocess.cpp b/src/libs/utils/synchronousprocess.cpp
index 70e65fc8c9..f197494b4c 100644
--- a/src/libs/utils/synchronousprocess.cpp
+++ b/src/libs/utils/synchronousprocess.cpp
@@ -271,7 +271,7 @@ public:
QTimer m_timer;
QEventLoop m_eventLoop;
SynchronousProcessResponse m_result;
- QString m_binary;
+ FilePath m_binary;
ChannelBuffer m_stdOut;
ChannelBuffer m_stdErr;
ExitCodeInterpreter m_exitCodeInterpreter = defaultExitCodeInterpreter;
@@ -293,7 +293,7 @@ void SynchronousProcessPrivate::clearForRun()
m_result.clear();
m_result.codec = m_codec;
m_startFailure = false;
- m_binary.clear();
+ m_binary = {};
}
// ----------- SynchronousProcess
@@ -446,12 +446,10 @@ static bool isGuiThread()
return QThread::currentThread() == QCoreApplication::instance()->thread();
}
-SynchronousProcessResponse SynchronousProcess::run(const QString &binary,
- const QStringList &args,
+SynchronousProcessResponse SynchronousProcess::run(const CommandLine &cmd,
const QByteArray &writeData)
{
- qCDebug(processLog).noquote() << "Starting:"
- << QtcProcess::joinArgs(QStringList(binary) + args);
+ qCDebug(processLog).noquote() << "Starting:" << cmd.toUserOutput();
ExecuteOnDestruction logResult([this] {
qCDebug(processLog) << d->m_result;
});
@@ -461,12 +459,12 @@ SynchronousProcessResponse SynchronousProcess::run(const QString &binary,
// On Windows, start failure is triggered immediately if the
// executable cannot be found in the path. Do not start the
// event loop in that case.
- d->m_binary = binary;
+ d->m_binary = cmd.executable();
// using QProcess::start() and passing program, args and OpenMode results in a different
// quoting of arguments than using QProcess::setArguments() beforehand and calling start()
// only with the OpenMode
- d->m_process.setProgram(binary);
- d->m_process.setArguments(args);
+ d->m_process.setProgram(cmd.executable().toString());
+ d->m_process.setArguments(cmd.splitArguments());
connect(&d->m_process, &QProcess::started, this, [this, writeData] {
if (!writeData.isEmpty()) {
int pos = 0;
@@ -503,10 +501,9 @@ SynchronousProcessResponse SynchronousProcess::run(const QString &binary,
return d->m_result;
}
-SynchronousProcessResponse SynchronousProcess::runBlocking(const QString &binary, const QStringList &args)
+SynchronousProcessResponse SynchronousProcess::runBlocking(const CommandLine &cmd)
{
- qCDebug(processLog).noquote() << "Starting blocking:"
- << QtcProcess::joinArgs(QStringList(binary) + args);
+ qCDebug(processLog).noquote() << "Starting blocking:" << cmd.toUserOutput();
ExecuteOnDestruction logResult([this] {
qCDebug(processLog) << d->m_result;
});
@@ -516,8 +513,8 @@ SynchronousProcessResponse SynchronousProcess::runBlocking(const QString &binary
// On Windows, start failure is triggered immediately if the
// executable cannot be found in the path. Do not start the
// event loop in that case.
- d->m_binary = binary;
- d->m_process.start(binary, args, QIODevice::ReadOnly);
+ d->m_binary = cmd.executable();
+ d->m_process.start(cmd.executable().toString(), cmd.splitArguments(), QIODevice::ReadOnly);
if (!d->m_process.waitForStarted(d->m_maxHangTimerCount * 1000)
&& d->m_process.state() == QProcess::NotRunning) {
d->m_result.result = SynchronousProcessResponse::StartFailed;
@@ -585,7 +582,7 @@ void SynchronousProcess::slotTimeout()
if (debug)
qDebug() << Q_FUNC_INFO << "HANG detected, killing";
d->m_waitingForUser = true;
- const bool terminate = !d->m_timeOutMessageBoxEnabled || askToKill(d->m_binary);
+ const bool terminate = !d->m_timeOutMessageBoxEnabled || askToKill(d->m_binary.toString());
d->m_waitingForUser = false;
if (terminate) {
SynchronousProcess::stopProcess(d->m_process);
diff --git a/src/libs/utils/synchronousprocess.h b/src/libs/utils/synchronousprocess.h
index d8e3275030..071e20e007 100644
--- a/src/libs/utils/synchronousprocess.h
+++ b/src/libs/utils/synchronousprocess.h
@@ -38,6 +38,7 @@ QT_FORWARD_DECLARE_CLASS(QDebug)
namespace Utils {
class SynchronousProcessPrivate;
+class CommandLine;
/* Result of SynchronousProcess execution */
class QTCREATOR_UTILS_EXPORT SynchronousProcessResponse
@@ -126,10 +127,10 @@ public:
void setExitCodeInterpreter(const ExitCodeInterpreter &interpreter);
ExitCodeInterpreter exitCodeInterpreter() const;
- // Starts an nested event loop and runs the binary with the arguments
- SynchronousProcessResponse run(const QString &binary, const QStringList &args, const QByteArray &writeData = {});
- // Starts the binary with the arguments blocking the UI fully
- SynchronousProcessResponse runBlocking(const QString &binary, const QStringList &args);
+ // Starts a nested event loop and runs the command
+ SynchronousProcessResponse run(const CommandLine &cmd, const QByteArray &writeData = {});
+ // Starts the command blocking the UI fully
+ SynchronousProcessResponse runBlocking(const CommandLine &cmd);
// Create a (derived) processes with flags applied.
static QSharedPointer<QProcess> createProcess(unsigned flags);
diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri
index a4697e0622..36f3aa14a3 100644
--- a/src/libs/utils/utils-lib.pri
+++ b/src/libs/utils/utils-lib.pri
@@ -27,6 +27,10 @@ SOURCES += \
$$PWD/environment.cpp \
$$PWD/environmentmodel.cpp \
$$PWD/environmentdialog.cpp \
+ $$PWD/namevaluedictionary.cpp \
+ $$PWD/namevalueitem.cpp \
+ $$PWD/namevaluemodel.cpp \
+ $$PWD/namevaluesdialog.cpp \
$$PWD/qrcparser.cpp \
$$PWD/qtcprocess.cpp \
$$PWD/reloadpromptutils.cpp \
@@ -125,19 +129,24 @@ SOURCES += \
$$PWD/fixedsizeclicklabel.cpp \
$$PWD/removefiledialog.cpp \
$$PWD/differ.cpp \
- $$PWD/jsontreeitem.cpp
-
+ $$PWD/jsontreeitem.cpp \
+ $$PWD/namevaluevalidator.cpp
win32:SOURCES += $$PWD/consoleprocess_win.cpp
else:SOURCES += $$PWD/consoleprocess_unix.cpp
HEADERS += \
+ $$PWD/environmentfwd.h \
$$PWD/genericconstants.h \
$$PWD/globalfilechangeblocker.h \
$$PWD/benchmarker.h \
$$PWD/environment.h \
$$PWD/environmentmodel.h \
$$PWD/environmentdialog.h \
+ $$PWD/namevaluedictionary.h \
+ $$PWD/namevalueitem.h \
+ $$PWD/namevaluemodel.h \
+ $$PWD/namevaluesdialog.h \
$$PWD/pointeralgorithm.h \
$$PWD/qrcparser.h \
$$PWD/qtcprocess.h \
@@ -269,7 +278,8 @@ HEADERS += \
$$PWD/differ.h \
$$PWD/cpplanguage_details.h \
$$PWD/jsontreeitem.h \
- $$PWD/listmodel.h
+ $$PWD/listmodel.h \
+ $$PWD/namevaluevalidator.h
FORMS += $$PWD/filewizardpage.ui \
$$PWD/newclasswidget.ui \
diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs
index 10368cd539..456d45a245 100644
--- a/src/libs/utils/utils.qbs
+++ b/src/libs/utils/utils.qbs
@@ -151,6 +151,16 @@ Project {
"macroexpander.cpp",
"macroexpander.h",
"mapreduce.h",
+ "namevaluedictionary.cpp",
+ "namevaluedictionary.h",
+ "namevalueitem.cpp",
+ "namevalueitem.h",
+ "namevaluemodel.cpp",
+ "namevaluemodel.h",
+ "namevaluesdialog.cpp",
+ "namevaluesdialog.h",
+ "namevaluevalidator.cpp",
+ "namevaluevalidator.h",
"navigationtreeview.cpp",
"navigationtreeview.h",
"networkaccessmanager.cpp",
@@ -202,7 +212,9 @@ Project {
"qtcprocess.h",
"reloadpromptutils.cpp",
"reloadpromptutils.h",
- "removefiledialog.cpp", "removefiledialog.h", "removefiledialog.ui",
+ "removefiledialog.cpp",
+ "removefiledialog.h",
+ "removefiledialog.ui",
"runextensions.cpp",
"runextensions.h",
"savedaction.cpp",