diff --git a/dist/changes-4.2.0.md b/dist/changes-4.2.0.md new file mode 100644 index 00000000000..d44ae8a81fa --- /dev/null +++ b/dist/changes-4.2.0.md @@ -0,0 +1,142 @@ +Qt Creator version 4.2 contains bug fixes and new features. + +The most important changes are listed in this document. For a complete +list of changes, see the Git log for the Qt Creator sources that +you can check out from the public Git repository. For example: + + git clone git://code.qt.io/qt-creator/qt-creator.git + git log --cherry-pick --pretty=oneline origin/4.1..v4.2.0 + +General + +* Added experimental editor for Qt SCXML +* Added pattern substitution for variable expansion + `%{variable/pattern/replacement}` (and `%{variable//pattern/replacement}` + for replacing multiple matches) +* Added default values for variable expansion (`%{variable:-default}`) +* Added Help > System Information for bug reporting purposes + (QTCREATORBUG-16135) +* Added option to hide the central widget in Debug mode + +Welcome + +* Added keyboard shortcuts for opening recent sessions and projects +* Improved performance when many sessions are shown + +Editing + +* Added action for selecting word under cursor (QTCREATORBUG-641) +* Fixed highlighting of Markdown files + (QTCREATORBUG-16304) + +Help + +* Added option to open link and current page in window (QTCREATORBUG-16842) + +All Projects + +* Reworked Projects mode UI +* Grouped all device options into one options category +* Added support for toolchains for different languages (currently C and C++) + +QMake Projects + +* Removed Qt Labs Controls wizard which is superseded by Qt Quick Controls 2 +* Fixed `Open with Designer` and `Open with Linguist` for mobile and embedded Qt + (QTCREATORBUG-16558) +* Fixed Add Library wizard when selecting library from absolute path or + different drive (QTCREATORBUG-8413, QTCREATORBUG-15732, QTCREATORBUG-16688) + +CMake Projects + +* Added support for CMake specific snippets +* Added support for platforms and toolsets +* Added warning for unsupported CMake versions +* Added drop down for selecting predefined values for properties +* Improved performance of opening project (QTCREATORBUG-16930) +* Made it possible to select CMake application on macOS +* Fixed that all unknown build target types were mapped to `ExecutableType` + +Qbs Projects + +* Made generated files available in project tree (QTCREATORBUG-15978) + +C++ Support + +* Added preview of images to tool tip on Qt resource URLs +* Added option to skip big files when indexing (QTCREATORBUG-16712) +* Added notification for parsing errors in headers +* Fixed `Move Definition to Class` for functions in template class and + template member functions (QTCREATORBUG-14354) +* Fixed issues with `Add Declaration`, `Add Definition`, and + `Move Definition Outside Class` for template functions +* Clang Code Model + * Improved responsiveness of completion and highlighting + +Debugging + +* Added pretty printing of `QRegExp` captures +* Added pretty printing of `QStaticStringData` +* Improved pretty printing of QV4 types +* Made display of maps more compact +* Fixed pretty printing of `QFixed` +* LLDB + * Added support for Qt Creator variables `%{...}` in startup commands + +QML Profiler + +* Added option to show memory usage and allocations as flame graph +* Added option to show vertical orientation lines in timeline + (click the time ruler) + +Qt Quick Designer + +* Added completion expression editor +* Added menu for editing `when` condition of states +* Added editor for managing C++ backend objects +* Added reformatting of `.ui.qml` files on save +* Added support for exporting single properties +* Added support for padding (Qt Quick 2.6) +* Added support for elide and various font properties to text items +* Fixed that it was not possible to give extracted components + the file extension `.ui.qml` +* Fixed that switching from Qt Quick Designer failed to commit pending changes + (QTCREATORBUG-14830) +* Fixed issues with pressing escape + +Diff Viewer + +* Added local diff for modified files in Qt Creator (`Diff` > + `Diff Current File`, `Diff` > `Diff All Modified Files`) + (QTCREATORBUG-9732) +* Fixed that reload prompt was shown when reverting change + +Version Control Systems + +* Gerrit + * Fixed pushing to Gerrit when remote repository is empty + (QTCREATORBUG-16780) + +Test Integration + +* Added option to disable crash handler when debugging +* Fixed that results were not shown when debugging (QTCREATORBUG-16693) +* Fixed that progress indicator sometimes did not stop + +Model Editor + +* Added zooming +* Added synchronization of selected diagram in diagram browser + +Platform Specific + +Android + +* Improved stability of determination if application is running +* Fixed that running without deployment did not start emulator + (QTCREATORBUG-10237) +* Fixed that permission model downgrade was not detected as error + (QTCREATORBUG-16630) +* Fixed handling of minimum required API level (QTCREATORBUG-16740) + +Credits for these changes go to: diff --git a/doc/images/qmldesigner-backends.png b/doc/images/qmldesigner-backends.png new file mode 100644 index 00000000000..b3e4a23d1b0 Binary files /dev/null and b/doc/images/qmldesigner-backends.png differ diff --git a/doc/images/qmldesigner-bindings.png b/doc/images/qmldesigner-bindings.png index ebe24eb7582..c3a90cc3c7d 100644 Binary files a/doc/images/qmldesigner-bindings.png and b/doc/images/qmldesigner-bindings.png differ diff --git a/doc/images/qmldesigner-connections.png b/doc/images/qmldesigner-connections.png index 6548e88e904..165385d9b28 100644 Binary files a/doc/images/qmldesigner-connections.png and b/doc/images/qmldesigner-connections.png differ diff --git a/doc/images/qmldesigner-dynamicprops.png b/doc/images/qmldesigner-dynamicprops.png index 04949ac6814..ae2125481ad 100644 Binary files a/doc/images/qmldesigner-dynamicprops.png and b/doc/images/qmldesigner-dynamicprops.png differ diff --git a/doc/src/qtcreator.qdoc b/doc/src/qtcreator.qdoc index 1f22d1d0ddd..20d07b2b6b3 100644 --- a/doc/src/qtcreator.qdoc +++ b/doc/src/qtcreator.qdoc @@ -124,6 +124,9 @@ \row \li {4,1} \note To report bugs and suggestions to the Qt Bug Tracker, select \uicontrol {Help > Report Bug}. + To copy and paste detailed information about your system to the + bug report, select \uicontrol Help > + \uicontrol {System Information}. You can also join the \QC mailing list at: \l{http://lists.qt-project.org/mailman/listinfo/} diff --git a/doc/src/qtquick/qtquick-connection-editor.qdoc b/doc/src/qtquick/qtquick-connection-editor.qdoc index a9ff30855b1..92062021cf8 100644 --- a/doc/src/qtquick/qtquick-connection-editor.qdoc +++ b/doc/src/qtquick/qtquick-connection-editor.qdoc @@ -41,6 +41,8 @@ \li Create bindings between the properties of two objects. + \li Manage backend QObjects. + \endlist For examples of adding connections, see @@ -58,6 +60,9 @@ \li Select the \uicontrol {Connections} tab. + \li Select the \inlineimage plus.png + (\uicontrol Add) button to add a connection. + \li Select \uicontrol Target to add the object to connect to a signal. \li Select \uicontrol {Signal Handler} to select the signal that the connection @@ -105,6 +110,9 @@ \li Select the \uicontrol {Bindings} tab. + \li Select the \inlineimage plus.png + (\uicontrol Add) button to add a binding. + \li Select \uicontrol Item to select the target object whose property you want to change dynamically. @@ -119,4 +127,53 @@ \endlist + \section1 Managing C++ Backend Objects + + Many applications provide QObject objects implemented in C++ that work as a + bridge between QML and C++. Such objects are typically registered with + qmlRegisterType or qmlRegisterSingletonType and then used by QML to + communicate with the C++ backend. Another example of such objects are the + state machines created by the \l {Using the Qt SCXML Compiler (qscxmlc)} + {Qt SCXML Compiler}. + + Backend objects in a QML file are accessible if the QML file contains the + required imports. In addition, for a non-singleton QObject, a dynamic + property that contains the QObject must be specified. + + A \e local QObject is instantiated in the current \e .qml file, as follows: + + \badcode + property MyType myType: MyType {}. + \endcode + + Otherwise the property is just defined, as follows: + + \badcode + property MyType myType + \endcode + + To manage backend objects: + + \list 1 + + \li Select the \uicontrol Backends tab to view accessible backend + objects. + + \image qmldesigner-backends.png + + \li Select the \inlineimage plus.png + (\uicontrol Add) button to add a backend object in the + \uicontrol {Add New C++ Backend} dialog. + + \li In the \uicontrol Type field, select the type of the backend QObject + to add. + + \li Select the \uicontrol {Define object locally} check box if the + QObject is not registered as a singleton. + + \li Select \uicontrol OK to add the required import and to create the + property for a non-singleton object. + + \endlist + */ diff --git a/doc/src/qtquick/qtquick-modules-with-plugins.qdoc b/doc/src/qtquick/qtquick-modules-with-plugins.qdoc index eab5678d641..c2def1cdc7a 100644 --- a/doc/src/qtquick/qtquick-modules-with-plugins.qdoc +++ b/doc/src/qtquick/qtquick-modules-with-plugins.qdoc @@ -43,8 +43,7 @@ information for code completion and the semantic checks to work correctly. When you write a QML module or use QML from a C++ application you typically - register new types with the \l{QQmlEngine} class \c qmlRegisterType() - function or expose some + register new types with the qmlRegisterType() function or expose some class instances with \l{QQmlContext::setContextProperty()}. The \QC C++ code model now scans for these calls and tells the QML code model about them. This means that properties are @@ -53,6 +52,9 @@ is available, and therefore, you must explicitly generate type information for QML modules with plugins before distributing them. + Classes registered with \c qmlRegisterType() can be used as backend objects + in the \QMLD. For more information, see \l {Adding Connections}. + Ideally, QML modules have a \c{plugins.qmltypes} file in the same directory as the \c qmldir file. The \c qmltypes file contains a description of the types exported by the module's plugins and is loaded by \QC when the diff --git a/qbs/modules/libclang/functions.js b/qbs/modules/libclang/functions.js index e09ebef1ee5..49f2512423d 100644 --- a/qbs/modules/libclang/functions.js +++ b/qbs/modules/libclang/functions.js @@ -1,5 +1,6 @@ var Environment = loadExtension("qbs.Environment") var File = loadExtension("qbs.File") +var FileInfo = loadExtension("qbs.FileInfo") var MinimumLLVMVersion = "3.8.0" var Process = loadExtension("qbs.Process") @@ -57,12 +58,12 @@ function llvmConfig(qbs, qtcFunctions) function includeDir(llvmConfig) { - return readOutput(llvmConfig, ["--includedir"]) + return FileInfo.fromNativeSeparators(readOutput(llvmConfig, ["--includedir"])); } function libDir(llvmConfig) { - return readOutput(llvmConfig, ["--libdir"]) + return FileInfo.fromNativeSeparators(readOutput(llvmConfig, ["--libdir"])); } function version(llvmConfig) diff --git a/scripts/deployqtHelper_mac.sh b/scripts/deployqtHelper_mac.sh index ca91e8dac27..231825f9ae4 100755 --- a/scripts/deployqtHelper_mac.sh +++ b/scripts/deployqtHelper_mac.sh @@ -144,8 +144,6 @@ if [ ! -d "$app_path/Contents/Frameworks/QtCore.framework" ]; then "-executable=$app_path/Contents/Resources/qtpromaker" \ "-executable=$app_path/Contents/Resources/sdktool" \ "-executable=$app_path/Contents/Resources/ios/iostool" \ - "-executable=$app_path/Contents/Resources/ios/iossim" \ - "-executable=$app_path/Contents/Resources/ios/iossim_1_8_2" \ "-executable=$app_path/Contents/Resources/buildoutputparser" \ "-executable=$app_path/Contents/Resources/cpaster" \ "-executable=$app_path/Contents/MacOS/qtdiag" \ diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/ColorButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/ColorButton.qml index 80272e25c34..203e692a82f 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/ColorButton.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/ColorButton.qml @@ -133,62 +133,35 @@ Item { fillMode: Image.Tile // note we smoothscale the shader from a smaller version to improve performance - ShaderEffect { - id: map + Canvas { + id: hubeBox opacity: colorButton.alpha - scale: surround.width / width; - layer.enabled: true - layer.smooth: true anchors.fill: parent property real hue: colorButton.hue + onHueChanged: requestPaint() - fragmentShader: " - varying mediump vec2 qt_TexCoord0; - uniform highp float qt_Opacity; - uniform highp float hue; + onPaint: { + var ctx = hubeBox.getContext('2d') - highp float hueToIntensity(highp float v1, highp float v2, highp float h) { - h = fract(h); - if (h < 1.0 / 6.0) - return v1 + (v2 - v1) * 6.0 * h; - else if (h < 1.0 / 2.0) - return v2; - else if (h < 2.0 / 3.0) - return v1 + (v2 - v1) * 6.0 * (2.0 / 3.0 - h); + ctx.save() - return v1; - } + ctx.clearRect(0, 0, hubeBox.width, hubeBox.height); - highp vec3 HSLtoRGB(highp vec3 color) { - highp float h = color.x; - highp float l = color.z; - highp float s = color.y; + for (var row = 0; row < hubeBox.height; row++){ + var gradient = ctx.createLinearGradient(0, 0, hubeBox.width,0); + var l = Math.abs(row - hubeBox.height) / hubeBox.height - if (s < 1.0 / 256.0) - return vec3(l, l, l); + gradient.addColorStop(0, Qt.hsla(hubeBox.hue, 0, l, 1)); + gradient.addColorStop(1, Qt.hsla(hubeBox.hue, 1, l, 1)); - highp float v1; - highp float v2; - if (l < 0.5) - v2 = l * (1.0 + s); - else - v2 = (l + s) - (s * l); + ctx.fillStyle = gradient; + ctx.fillRect(0, row, hubeBox.width, 1); + } - v1 = 2.0 * l - v2; + ctx.restore() - highp float d = 1.0 / 3.0; - highp float r = hueToIntensity(v1, v2, h + d); - highp float g = hueToIntensity(v1, v2, h); - highp float b = hueToIntensity(v1, v2, h - d); - return vec3(r, g, b); - } + } - void main() { - lowp vec4 c = vec4(1.0); - c.rgb = HSLtoRGB(vec3(hue, 1.0 - qt_TexCoord0.t, qt_TexCoord0.s)); - gl_FragColor = c * qt_Opacity; - } - " } Canvas { @@ -215,9 +188,8 @@ Item { context.clearRect(0, 0, canvas.width, canvas.height); - var yy = canvas.height - colorButton.saturation * canvas.height - - var xx = colorButton.lightness * canvas.width + var yy = canvas.height -colorButton.lightness * canvas.height + var xx = colorButton.saturation * canvas.width ctx.strokeStyle = canvas.strokeStyle ctx.lineWidth = 1 @@ -245,13 +217,9 @@ Item { if (pressed) { var xx = Math.max(0, Math.min(mouse.x, parent.width)) var yy = Math.max(0, Math.min(mouse.y, parent.height)) - //saturationSlider.value = 1.0 - yy / parent.height - //lightnessSlider.value = xx / parent.width - //var myHue = colorButton.hue - colorButton.saturation = 1.0 - yy / parent.height; - colorButton.lightness = xx / parent.width; - //colorButton.hue = myHue - //colorButton.color = Qt.hsla(colorButton.hue, 1.0 - yy / parent.height, xx / parent.width, colorButton.alpha) + + colorButton.lightness = 1.0 - yy / parent.height; + colorButton.saturation = xx / parent.width; } } onPressed: positionChanged(mouse) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/ColorEditor.qml index 007de033723..baf54c818da 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/ColorEditor.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/ColorEditor.qml @@ -123,6 +123,7 @@ Column { if (supportGradient && gradientLine.hasGradient) { colorEditor.color = gradientLine.currentColor gradientLine.currentColor = color + textField.text = colorEditor.color } gradientLine.isInValidState = true } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/UrlChooser.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/UrlChooser.qml index c0e10a1ee4c..f6bcae25a3d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/UrlChooser.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/HelperWidgets/UrlChooser.qml @@ -56,7 +56,7 @@ RowLayout { x: 2 anchors.verticalCenter: parent.verticalCenter backendValue: urlChooser.backendValue - visible: comboBox.enabled + visible: urlChooser.enabled } property bool isComplete: false @@ -92,16 +92,25 @@ RowLayout { setCurrentText(textValue) } + onAccepted: { + if (!comboBox.isComplete) + return; - onCurrentTextChanged: { + if (backendValue.value !== currentText) + backendValue.value = currentText; + } + + onActivated: { + var cText = textAt(index) + print(cText) if (backendValue === undefined) return; if (!comboBox.isComplete) return; - if (backendValue.value !== currentText) - backendValue.value = currentText; + if (backendValue.value !== cText) + backendValue.value = cText; } Component.onCompleted: { @@ -158,7 +167,8 @@ RowLayout { onClicked: { darkPanel.opacity = 1 fileModel.openFileDialog() - backendValue.value = fileModel.fileName + if (fileModel.fileName != "") + backendValue.value = fileModel.fileName darkPanel.opacity = 0 } } diff --git a/src/libs/clangbackendipc/clangcodemodelconnectionclient.cpp b/src/libs/clangbackendipc/clangcodemodelconnectionclient.cpp index ed969ec83e7..4256be360d0 100644 --- a/src/libs/clangbackendipc/clangcodemodelconnectionclient.cpp +++ b/src/libs/clangbackendipc/clangcodemodelconnectionclient.cpp @@ -43,8 +43,8 @@ ClangCodeModelConnectionClient::ClangCodeModelConnectionClient( ClangCodeModelClientInterface *client) : serverProxy_(client, ioDevice()) { - stdErrPrefixer().setPrefix("ClangCodeModelConnectionClient.stderr: "); - stdOutPrefixer().setPrefix("ClangCodeModelConnectionClient.stdout: "); + stdErrPrefixer().setPrefix("clangbackend.stderr: "); + stdOutPrefixer().setPrefix("clangbackend.stdout: "); } ClangCodeModelConnectionClient::~ClangCodeModelConnectionClient() diff --git a/src/libs/clangbackendipc/connectionclient.cpp b/src/libs/clangbackendipc/connectionclient.cpp index d0ba95bc580..0ccd742fb6c 100644 --- a/src/libs/clangbackendipc/connectionclient.cpp +++ b/src/libs/clangbackendipc/connectionclient.cpp @@ -43,6 +43,7 @@ namespace ClangBackEnd { ConnectionClient::ConnectionClient() { processAliveTimer.setInterval(10000); + resetTemporaryDir(); static const bool startAliveTimer = !qEnvironmentVariableIntValue("QTC_CLANG_NO_ALIVE_TIMER"); @@ -113,9 +114,7 @@ QProcessEnvironment ConnectionClient::processEnvironment() const const QTemporaryDir &ConnectionClient::temporaryDirectory() const { - static QTemporaryDir temporaryDirectory(QDir::tempPath() + QStringLiteral("/qtc-clang-XXXXXX")); - - return temporaryDirectory; + return *temporaryDirectory_.data(); } LinePrefixer &ConnectionClient::stdErrPrefixer() @@ -147,6 +146,7 @@ void ConnectionClient::restartProcessAsynchronously() { if (!processIsStarting) { finishProcess(std::move(process_)); + resetTemporaryDir(); // clear left-over preambles startProcessAndConnectToServerAsynchronously(); } @@ -218,6 +218,12 @@ void ConnectionClient::printStandardError() qDebug("%s", stdErrPrefixer_.prefix(process_->readAllStandardError()).constData()); } +void ConnectionClient::resetTemporaryDir() +{ + const QString templatePath = QDir::tempPath() + QStringLiteral("/qtc-clang-XXXXXX"); + temporaryDirectory_.reset(new QTemporaryDir(templatePath)); +} + void ConnectionClient::connectLocalSocketConnected() { connect(&localSocket, diff --git a/src/libs/clangbackendipc/connectionclient.h b/src/libs/clangbackendipc/connectionclient.h index b3eed0703b5..74ea4cf3298 100644 --- a/src/libs/clangbackendipc/connectionclient.h +++ b/src/libs/clangbackendipc/connectionclient.h @@ -30,6 +30,8 @@ #include #include +#include +#include #include @@ -102,6 +104,8 @@ private: void printStandardOutput(); void printStandardError(); + void resetTemporaryDir(); + void connectLocalSocketConnected(); void connectLocalSocketDisconnected(); void connectProcessFinished(QProcess *process) const; @@ -121,6 +125,7 @@ private: mutable std::unique_ptr process_; QLocalSocket localSocket; + QScopedPointer temporaryDirectory_; QTimer processAliveTimer; QString processPath_; bool isAliveTimerResetted = false; diff --git a/src/libs/clangbackendipc/diagnosticcontainer.h b/src/libs/clangbackendipc/diagnosticcontainer.h index b9d47db04e1..677fb813d5a 100644 --- a/src/libs/clangbackendipc/diagnosticcontainer.h +++ b/src/libs/clangbackendipc/diagnosticcontainer.h @@ -144,6 +144,11 @@ public: && first.location_ == second.location_; } + friend bool operator!=(const DiagnosticContainer &first, const DiagnosticContainer &second) + { + return !(first == second); + } + private: SourceLocationContainer location_; QVector ranges_; diff --git a/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp b/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp index 8af722de62b..f6271ff14d3 100644 --- a/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp +++ b/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp @@ -76,20 +76,17 @@ DocumentController::DocumentController(QObject *parent) : { // project controller connect(m_projectController, &ProjectController::changed, this, &DocumentController::changed); - connect(m_projectController, &ProjectController::modificationChanged, this, &DocumentController::modificationChanged); // model controller m_modelController->setUndoController(m_undoController); - connect(m_modelController, &ModelController::modified, [this](){ - m_projectController->setModified(true); - }); + connect(m_modelController, &ModelController::modified, + m_projectController, &ProjectController::setModified); // diagram controller m_diagramController->setModelController(m_modelController); m_diagramController->setUndoController(m_undoController); - connect(m_diagramController, &DiagramController::modified, [this](){ - m_projectController->setModified(true); - }); + connect(m_diagramController, &DiagramController::modified, + m_projectController, &ProjectController::setModified); // diagram scene controller m_diagramSceneController->setModelController(m_modelController); diff --git a/src/libs/modelinglib/qmt/document_controller/documentcontroller.h b/src/libs/modelinglib/qmt/document_controller/documentcontroller.h index b9eb3a53719..d9a253773f9 100644 --- a/src/libs/modelinglib/qmt/document_controller/documentcontroller.h +++ b/src/libs/modelinglib/qmt/document_controller/documentcontroller.h @@ -61,7 +61,6 @@ public: signals: void changed(); - void modificationChanged(bool modified); void modelClipboardChanged(bool isEmpty); void diagramClipboardChanged(bool isEmpty); diff --git a/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp b/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp index 384bb52281a..9698a2f601f 100644 --- a/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp +++ b/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp @@ -43,7 +43,8 @@ ProjectIsModifiedException::ProjectIsModifiedException() } ProjectController::ProjectController(QObject *parent) - : QObject(parent) + : QObject(parent), + m_isModified(false) { } @@ -58,7 +59,7 @@ void ProjectController::newProject(const QString &fileName) rootPackage->setName(tr("Model")); m_project->setRootPackage(rootPackage); m_project->setFileName(fileName); - setModified(false); + m_isModified = false; emit fileNameChanged(m_project->fileName()); emit changed(); } @@ -67,18 +68,17 @@ void ProjectController::setFileName(const QString &fileName) { if (fileName != m_project->fileName()) { m_project->setFileName(fileName); - setModified(true); + setModified(); emit fileNameChanged(m_project->fileName()); } } -void ProjectController::setModified(bool modified) +void ProjectController::setModified() { - if (m_isModified == modified) - return; - - m_isModified = modified; - emit modificationChanged(modified); + if (!m_isModified) { + m_isModified = true; + emit changed(); + } } void ProjectController::load() @@ -89,7 +89,7 @@ void ProjectController::load() throw NoFileNameException(); ProjectSerializer serializer; serializer.load(m_project->fileName(), m_project.data()); - setModified(false); + m_isModified = false; emit changed(); } @@ -99,7 +99,7 @@ void ProjectController::save() throw NoFileNameException(); ProjectSerializer serializer; serializer.save(m_project->fileName(), m_project.data()); - setModified(false); + m_isModified = false; emit changed(); } diff --git a/src/libs/modelinglib/qmt/project_controller/projectcontroller.h b/src/libs/modelinglib/qmt/project_controller/projectcontroller.h index a4eb47f9a60..73daf2a2ac9 100644 --- a/src/libs/modelinglib/qmt/project_controller/projectcontroller.h +++ b/src/libs/modelinglib/qmt/project_controller/projectcontroller.h @@ -58,7 +58,6 @@ public: signals: void changed(); void fileNameChanged(const QString &fileName); - void modificationChanged(bool modified); public: Project *project() const { return m_project.data(); } @@ -66,7 +65,7 @@ public: void newProject(const QString &fileName); void setFileName(const QString &fileName); - void setModified(bool modified); + void setModified(); void load(); void save(); @@ -74,7 +73,7 @@ public: private: QScopedPointer m_project; - bool m_isModified = false; + bool m_isModified; }; } // namespace qmt diff --git a/src/libs/qtcreatorcdbext/pytype.cpp b/src/libs/qtcreatorcdbext/pytype.cpp index efa998b5cbb..7e1010a9d93 100644 --- a/src/libs/qtcreatorcdbext/pytype.cpp +++ b/src/libs/qtcreatorcdbext/pytype.cpp @@ -87,12 +87,14 @@ char *getTypeName(ULONG64 module, ULONG typeId) symbols->GetTypeName(module, typeId, NULL, 0, &size); if (size > 0) { typeName = new char[size]; - if (FAILED(symbols->GetTypeName(module, typeId, typeName, size, &size))) { + if (SUCCEEDED(symbols->GetTypeName(module, typeId, typeName, size, &size))) + return typeName; + else delete[] typeName; - typeName = new char[1]; - typeName[0] = 0; - } } + typeName = new char[1]; + typeName[0] = 0; + return typeName; } @@ -270,6 +272,24 @@ PyObject *type_TemplateArgument(Type *self, PyObject *args) return lookupType(innerType); } +PyObject *type_TemplateArguments(Type *self) +{ + std::vector innerTypes = innerTypesOf(getTypeName(self)); + auto templateArguments = PyList_New(0); + for (const std::string &innerType : innerTypes) { + PyObject* childValue; + try { + int integer = std::stoi(innerType); + childValue = Py_BuildValue("i", integer); + } + catch (std::invalid_argument) { + childValue = lookupType(innerType); + } + PyList_Append(templateArguments, childValue); + } + return templateArguments; +} + PyObject *type_New(PyTypeObject *type, PyObject *, PyObject *) { Type *self = reinterpret_cast(type->tp_alloc(type, 0)); @@ -313,6 +333,8 @@ static PyMethodDef typeMethods[] = { {"templateArgument", PyCFunction(type_TemplateArgument), METH_VARARGS, "Returns template argument at position"}, + {"templateArguments", PyCFunction(type_TemplateArguments), METH_NOARGS, + "Returns all template arguments."}, {NULL} /* Sentinel */ }; diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index af259aa5460..5cf04d9441b 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -282,7 +282,7 @@ extern "C" HRESULT CALLBACK pid(CIDebugClient *client, PCSTR args) int token; commandTokens(args, &token); - dprintf("Qt Creator CDB extension version 4.0 %d bit.\n", + dprintf("Qt Creator CDB extension version 4.2 %d bit.\n", sizeof(void *) * 8); if (const ULONG pid = currentProcessId(client)) ExtensionContext::instance().report('R', token, 0, "pid", "%u", pid); diff --git a/src/libs/utils/guard.cpp b/src/libs/utils/guard.cpp new file mode 100644 index 00000000000..925bdb226f9 --- /dev/null +++ b/src/libs/utils/guard.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "guard.h" +#include "qtcassert.h" + +/*! \class Utils::Guard + + \brief The Guard class implements a recursive guard with locking mechanism. + + It may be used as an alternative to QSignalBlocker. + QSignalBlocker blocks all signals of the object + which is usually not desirable. It may also block signals + which are needed internally by the object itself. + The Guard and GuardLocker classes don't block signals at all. + + When calling a object's method which may in turn emit a signal + which you are connected to, and you want to ignore + this notification, you should keep the Guard object + as your class member and declare the GuardLocker object + just before calling the mentioned method, like: + + \code + class MyClass : public QObject + { + \dots + private: + Guard updateGuard; // member of your class + }; + + \dots + + void MyClass::updateOtherObject() + { + GuardLocker updatelocker(updateGuard); + otherObject->update(); // this may trigger a signal + } + \endcode + + Inside a slot which is connected to the other's object signal + you may check if the guard is locked and ignore the further + operations in this case: + + \code + void MyClass::otherObjectUpdated() + { + if (updateGuard.isLocked) + return; + + // we didn't trigger the update + // so do update now + \dots + } + \endcode + + The GuardLock unlocks the Guard in it's destructor. + + The Guard object is recursive, you may declare many GuardLocker + objects for the same Guard instance and the Guard will be locked + as long as at least one GuardLocker object created for the Guard + is in scope. +*/ + +namespace Utils { + +Guard::Guard() +{ +} + +Guard::~Guard() +{ + QTC_CHECK(m_lockCount == 0); +} + +bool Guard::isLocked() const +{ + return m_lockCount; +} + +GuardLocker::GuardLocker(Guard &guard) + : m_guard(guard) +{ + ++m_guard.m_lockCount; +} + +GuardLocker::~GuardLocker() +{ + --m_guard.m_lockCount; +} + +} // namespace Utils diff --git a/src/libs/utils/guard.h b/src/libs/utils/guard.h new file mode 100644 index 00000000000..063825650dc --- /dev/null +++ b/src/libs/utils/guard.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "utils_global.h" +#include + +namespace Utils { + +class QTCREATOR_UTILS_EXPORT Guard +{ + Q_DISABLE_COPY(Guard) +public: + Guard(); + ~Guard(); + bool isLocked() const; +private: + int m_lockCount = 0; + friend class GuardLocker; +}; + +class QTCREATOR_UTILS_EXPORT GuardLocker +{ + Q_DISABLE_COPY(GuardLocker) +public: + GuardLocker(Guard &guard); + ~GuardLocker(); + +private: + Guard &m_guard; +}; + +} // namespace Utils diff --git a/src/libs/utils/images/snapshot.png b/src/libs/utils/images/snapshot.png new file mode 100644 index 00000000000..c8a731d41e1 Binary files /dev/null and b/src/libs/utils/images/snapshot.png differ diff --git a/src/libs/utils/images/snapshot@2x.png b/src/libs/utils/images/snapshot@2x.png new file mode 100644 index 00000000000..b6e7e6afadc Binary files /dev/null and b/src/libs/utils/images/snapshot@2x.png differ diff --git a/src/libs/utils/reloadpromptutils.cpp b/src/libs/utils/reloadpromptutils.cpp index 8eec9e92e1b..798a4876a70 100644 --- a/src/libs/utils/reloadpromptutils.cpp +++ b/src/libs/utils/reloadpromptutils.cpp @@ -35,6 +35,7 @@ namespace Utils { QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const FileName &fileName, bool modified, + bool enableDiffOption, QWidget *parent) { @@ -50,12 +51,13 @@ QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const FileName &fileName, "The file %1 has changed outside Qt Creator. Do you want to reload it?"); } msg = msg.arg(fileName.fileName()); - return reloadPrompt(title, msg, fileName.toUserOutput(), parent); + return reloadPrompt(title, msg, fileName.toUserOutput(), enableDiffOption, parent); } QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const QString &title, const QString &prompt, const QString &details, + bool enableDiffOption, QWidget *parent) { QMessageBox msg(parent); @@ -69,7 +71,19 @@ QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const QString &title, msg.button(QMessageBox::Close)->setText(QCoreApplication::translate("Utils::reloadPrompt", "&Close")); - switch (msg.exec()) { + QPushButton *diffButton = nullptr; + if (enableDiffOption) { + diffButton = msg.addButton(QCoreApplication::translate( + "Utils::reloadPrompt", "No to All && &Diff"), + QMessageBox::NoRole); + } + + const int result = msg.exec(); + + if (msg.clickedButton() == diffButton) + return ReloadNoneAndDiff; + + switch (result) { case QMessageBox::Yes: return ReloadCurrent; case QMessageBox::YesToAll: diff --git a/src/libs/utils/reloadpromptutils.h b/src/libs/utils/reloadpromptutils.h index b01bd25b53b..8991d52b59b 100644 --- a/src/libs/utils/reloadpromptutils.h +++ b/src/libs/utils/reloadpromptutils.h @@ -40,15 +40,19 @@ enum ReloadPromptAnswer { ReloadAll, ReloadSkipCurrent, ReloadNone, + ReloadNoneAndDiff, CloseCurrent }; QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const FileName &fileName, bool modified, + bool enableDiffOption, QWidget *parent); QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const QString &title, const QString &prompt, - const QString &details, QWidget *parent); + const QString &details, + bool enableDiffOption, + QWidget *parent); enum FileDeletedPromptAnswer { FileDeletedClose, diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri index fc7b552ff67..8442d7c351c 100644 --- a/src/libs/utils/utils-lib.pri +++ b/src/libs/utils/utils-lib.pri @@ -100,7 +100,8 @@ SOURCES += $$PWD/environment.cpp \ $$PWD/icon.cpp \ $$PWD/port.cpp \ $$PWD/runextensions.cpp \ - $$PWD/utilsicons.cpp + $$PWD/utilsicons.cpp \ + $$PWD/guard.cpp win32:SOURCES += $$PWD/consoleprocess_win.cpp else:SOURCES += $$PWD/consoleprocess_unix.cpp @@ -217,7 +218,8 @@ HEADERS += \ $$PWD/smallstringvector.h \ $$PWD/smallstringlayout.h \ $$PWD/sizedarray.h \ - $$PWD/smallstringio.h + $$PWD/smallstringio.h \ + $$PWD/guard.h FORMS += $$PWD/filewizardpage.ui \ $$PWD/projectintropage.ui \ diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index 54a2f71b840..3bf52fc8c6b 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -114,6 +114,8 @@ Project { "flowlayout.cpp", "flowlayout.h", "functiontraits.h", + "guard.cpp", + "guard.h", "historycompleter.cpp", "historycompleter.h", "hostosinfo.h", diff --git a/src/libs/utils/utils.qrc b/src/libs/utils/utils.qrc index 39aea443845..6378073fc12 100644 --- a/src/libs/utils/utils.qrc +++ b/src/libs/utils/utils.qrc @@ -159,5 +159,7 @@ images/iconoverlay_warning_background@2x.png images/bookmark.png images/bookmark@2x.png + images/snapshot.png + images/snapshot@2x.png diff --git a/src/libs/utils/utilsicons.cpp b/src/libs/utils/utilsicons.cpp index 2117acb09cb..e81aa34af7f 100644 --- a/src/libs/utils/utilsicons.cpp +++ b/src/libs/utils/utilsicons.cpp @@ -63,6 +63,8 @@ const Icon BOOKMARK_TOOLBAR({ {QLatin1String(":/utils/images/bookmark.png"), Theme::IconsBaseColor}}); const Icon BOOKMARK_TEXTEDITOR({ {QLatin1String(":/utils/images/bookmark.png"), Theme::Bookmarks_TextMarkColor}}, Icon::Tint); +const Icon SNAPSHOT_TOOLBAR({ + {QLatin1String(":/utils/images/snapshot.png"), Theme::IconsBaseColor}}); const Icon NEWFILE({ {QLatin1String(":/utils/images/filenew.png"), Theme::PanelTextColorMid}}, Icon::Tint); diff --git a/src/libs/utils/utilsicons.h b/src/libs/utils/utilsicons.h index 24c99bfb2bd..8a1dc4ed04c 100644 --- a/src/libs/utils/utilsicons.h +++ b/src/libs/utils/utilsicons.h @@ -48,6 +48,7 @@ QTCREATOR_UTILS_EXPORT extern const Icon ERROR; QTCREATOR_UTILS_EXPORT extern const Icon BOOKMARK; QTCREATOR_UTILS_EXPORT extern const Icon BOOKMARK_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon BOOKMARK_TEXTEDITOR; +QTCREATOR_UTILS_EXPORT extern const Icon SNAPSHOT_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon NEWFILE; QTCREATOR_UTILS_EXPORT extern const Icon OPENFILE; diff --git a/src/plugins/android/androidmanifestdocument.cpp b/src/plugins/android/androidmanifestdocument.cpp index 01b1e3d8106..e3473a891b0 100644 --- a/src/plugins/android/androidmanifestdocument.cpp +++ b/src/plugins/android/androidmanifestdocument.cpp @@ -43,12 +43,8 @@ AndroidManifestDocument::AndroidManifestDocument(AndroidManifestEditorWidget *ed setId(Constants::ANDROID_MANIFEST_EDITOR_ID); setMimeType(QLatin1String(Constants::ANDROID_MANIFEST_MIME_TYPE)); setSuspendAllowed(false); - connect(editorWidget, &AndroidManifestEditorWidget::modificationChanged, - this, &Core::IDocument::setModified); - connect(this, &Core::IDocument::modificationChanged, - editorWidget, &AndroidManifestEditorWidget::setModified); - - setModified(editorWidget->isModified()); + connect(editorWidget, &AndroidManifestEditorWidget::guiChanged, + this, &Core::IDocument::changed); } bool AndroidManifestDocument::save(QString *errorString, const QString &fileName, bool autoSave) @@ -59,6 +55,11 @@ bool AndroidManifestDocument::save(QString *errorString, const QString &fileName return result; } +bool AndroidManifestDocument::isModified() const +{ + return TextDocument::isModified() || m_editorWidget->isModified(); +} + bool AndroidManifestDocument::isSaveAsAllowed() const { return false; diff --git a/src/plugins/android/androidmanifestdocument.h b/src/plugins/android/androidmanifestdocument.h index 28b8bc8306e..169047f0516 100644 --- a/src/plugins/android/androidmanifestdocument.h +++ b/src/plugins/android/androidmanifestdocument.h @@ -39,6 +39,7 @@ public: bool save(QString *errorString, const QString &fileName = QString(), bool autoSave = false) override; + bool isModified() const override; bool isSaveAsAllowed() const override; private: diff --git a/src/plugins/android/androidmanifesteditorwidget.cpp b/src/plugins/android/androidmanifesteditorwidget.cpp index 6c8f7670715..a2d1d8beed8 100644 --- a/src/plugins/android/androidmanifesteditorwidget.cpp +++ b/src/plugins/android/androidmanifesteditorwidget.cpp @@ -101,7 +101,7 @@ Project *androidProject(const Utils::FileName &fileName) AndroidManifestEditorWidget::AndroidManifestEditorWidget() : QStackedWidget(), - m_modified(false), + m_dirty(false), m_stayClean(false) { m_textEditorWidget = new AndroidManifestTextEditorWidget(this); @@ -138,7 +138,7 @@ void AndroidManifestEditorWidget::initializePage() QGroupBox *packageGroupBox = new QGroupBox(mainWidget); topLayout->addWidget(packageGroupBox); - auto setDirtyFunc = [this] { setModified(); }; + auto setDirtyFunc = [this] { setDirty(); }; packageGroupBox->setTitle(tr("Package")); { QFormLayout *formLayout = new QFormLayout(); @@ -206,7 +206,7 @@ void AndroidManifestEditorWidget::initializePage() connect(m_packageNameLineEdit, &QLineEdit::textEdited, this, &AndroidManifestEditorWidget::setPackageName); connect(m_versionCode, static_cast(&QSpinBox::valueChanged), - this, &AndroidManifestEditorWidget::setModified); + this, &AndroidManifestEditorWidget::setDirty); connect(m_versionNameLinedit, &QLineEdit::textEdited, this, setDirtyFunc); connect(m_androidMinSdkVersion, @@ -524,17 +524,17 @@ void AndroidManifestEditorWidget::updateAfterFileLoad() setActivePage(Source); } -void AndroidManifestEditorWidget::setModified(bool modified) +void AndroidManifestEditorWidget::setDirty(bool dirty) { - if (m_stayClean || modified == m_modified) + if (m_stayClean || dirty == m_dirty) return; - m_modified = modified; - emit modificationChanged(modified); + m_dirty = dirty; + emit guiChanged(); } bool AndroidManifestEditorWidget::isModified() const { - return m_modified + return m_dirty || !m_hIconPath.isEmpty() || !m_mIconPath.isEmpty() || !m_lIconPath.isEmpty(); @@ -819,7 +819,7 @@ void AndroidManifestEditorWidget::syncToWidgets(const QDomDocument &doc) updateAddRemovePermissionButtons(); m_stayClean = false; - m_modified = false; + m_dirty = false; } int extractVersion(const QString &string) @@ -862,7 +862,7 @@ void AndroidManifestEditorWidget::syncToEditor() m_textEditorWidget->setPlainText(result); m_textEditorWidget->document()->setModified(true); - m_modified = false; + m_dirty = false; } namespace { @@ -1253,7 +1253,7 @@ void AndroidManifestEditorWidget::setLDPIIcon() return; m_lIconPath = file; m_lIconButton->setIcon(QIcon(file)); - setModified(true); + setDirty(true); } void AndroidManifestEditorWidget::setMDPIIcon() @@ -1263,7 +1263,7 @@ void AndroidManifestEditorWidget::setMDPIIcon() return; m_mIconPath = file; m_mIconButton->setIcon(QIcon(file)); - setModified(true); + setDirty(true); } void AndroidManifestEditorWidget::setHDPIIcon() @@ -1273,12 +1273,12 @@ void AndroidManifestEditorWidget::setHDPIIcon() return; m_hIconPath = file; m_hIconButton->setIcon(QIcon(file)); - setModified(true); + setDirty(true); } void AndroidManifestEditorWidget::defaultPermissionOrFeatureCheckBoxClicked() { - setModified(true); + setDirty(true); } void AndroidManifestEditorWidget::updateAddRemovePermissionButtons() @@ -1293,7 +1293,7 @@ void AndroidManifestEditorWidget::addPermission() { m_permissionsModel->addPermission(m_permissionsComboBox->currentText()); updateAddRemovePermissionButtons(); - setModified(true); + setDirty(true); } void AndroidManifestEditorWidget::removePermission() @@ -1302,7 +1302,7 @@ void AndroidManifestEditorWidget::removePermission() if (idx.isValid()) m_permissionsModel->removePermission(idx.row()); updateAddRemovePermissionButtons(); - setModified(true); + setDirty(true); } void AndroidManifestEditorWidget::setPackageName() @@ -1312,7 +1312,7 @@ void AndroidManifestEditorWidget::setPackageName() bool valid = checkPackageName(packageName); m_packageNameWarning->setVisible(!valid); m_packageNameWarningIcon->setVisible(!valid); - setModified(true); + setDirty(true); } diff --git a/src/plugins/android/androidmanifesteditorwidget.h b/src/plugins/android/androidmanifesteditorwidget.h index 899393ca349..9dab5e8d1a3 100644 --- a/src/plugins/android/androidmanifesteditorwidget.h +++ b/src/plugins/android/androidmanifesteditorwidget.h @@ -101,10 +101,10 @@ public: Core::IEditor *editor() const; TextEditor::TextEditorWidget *textEditorWidget() const; - void setModified(bool modified = true); + void setDirty(bool dirty = true); signals: - void modificationChanged(bool modified); + void guiChanged(); protected: bool eventFilter(QObject *obj, QEvent *event); @@ -150,7 +150,7 @@ private: QString parseComment(QXmlStreamReader &reader, QXmlStreamWriter &writer); void parseUnknownElement(QXmlStreamReader &reader, QXmlStreamWriter &writer); - bool m_modified; // indicates that we need to call syncToEditor() + bool m_dirty; // indicates that we need to call syncToEditor() bool m_stayClean; int m_errorLine; int m_errorColumn; diff --git a/src/plugins/bineditor/bineditorplugin.cpp b/src/plugins/bineditor/bineditorplugin.cpp index 050bbcb6971..5fcccd78386 100644 --- a/src/plugins/bineditor/bineditorplugin.cpp +++ b/src/plugins/bineditor/bineditorplugin.cpp @@ -320,6 +320,12 @@ public: } public: + bool isModified() const override + { + return isTemporary()/*e.g. memory view*/ ? false + : m_widget->isModified(); + } + bool isFileReadOnly() const override { const FileName fn = filePath(); if (fn.isEmpty()) @@ -385,12 +391,7 @@ public: connect(m_addressEdit, &QLineEdit::editingFinished, this, &BinEditor::jumpToAddress); connect(widget, &BinEditorWidget::modificationChanged, - m_file, &IDocument::setModified); - connect(m_file, &IDocument::modificationChanged, - widget, &BinEditorWidget::setModified); - - m_file->setModified(widget->isModified()); - + m_file, &IDocument::changed); updateCursorPosition(widget->cursorPosition()); } diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp index 831a3bdea62..ee2d0ea43f7 100644 --- a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp +++ b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp @@ -69,6 +69,7 @@ #include +#include #include #include #include @@ -691,8 +692,10 @@ void IpcCommunicator::logRestartedDueToUnexpectedFinish() void IpcCommunicator::logError(const QString &text) { - Core::MessageManager::write(text, Core::MessageManager::Flash); - qWarning("%s", qPrintable(text)); + const QString textWithTimestamp = QDateTime::currentDateTime().toString(Qt::ISODate) + + ' ' + text; + Core::MessageManager::write(textWithTimestamp, Core::MessageManager::Flash); + qWarning("%s", qPrintable(textWithTimestamp)); } void IpcCommunicator::initializeBackendWithCurrentData() diff --git a/src/plugins/clangcodemodel/clangcodemodel.pro b/src/plugins/clangcodemodel/clangcodemodel.pro index ff3ce0aea49..0d9b5ee96b7 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.pro +++ b/src/plugins/clangcodemodel/clangcodemodel.pro @@ -3,7 +3,8 @@ include(../../shared/clang/clang_installation.pri) # The following defines are used to determine the clang include path for intrinsics. DEFINES += CLANG_VERSION=\\\"$${LLVM_VERSION}\\\" -DEFINES += "\"CLANG_RESOURCE_DIR=\\\"$${LLVM_LIBDIR}/clang/$${LLVM_VERSION}/include\\\"\"" +CLANG_RESOURCE_DIR=$$clean_path($${LLVM_LIBDIR}/clang/$${LLVM_VERSION}/include) +DEFINES += "\"CLANG_RESOURCE_DIR=\\\"$${CLANG_RESOURCE_DIR}\\\"\"" SOURCES += \ clangactivationsequencecontextprocessor.cpp \ diff --git a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp index 5b6c48c9536..1275ab81cd3 100644 --- a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp +++ b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp @@ -28,34 +28,22 @@ #include +#include #include +#include +#include #include -#include +#include #include -#include -#include + +using namespace ClangCodeModel; +using Internal::ClangDiagnosticWidget; namespace { const char LINK_ACTION_GOTO_LOCATION[] = "#gotoLocation"; const char LINK_ACTION_APPLY_FIX[] = "#applyFix"; -const int childIndentationOnTheLeftInPixel = 10; - -QString wrapInBoldTags(const QString &text) -{ - return QStringLiteral("") + text + QStringLiteral(""); -} - -QString wrapInLink(const QString &text, const QString &target) -{ - return QStringLiteral("%2").arg(target, text); -} - -QString wrapInColor(const QString &text, const QByteArray &color) -{ - return QStringLiteral("%1").arg(text, QString::fromUtf8(color)); -} QString fileNamePrefix(const QString &mainFilePath, const ClangBackEnd::SourceLocationContainer &location) @@ -74,181 +62,293 @@ QString locationToString(const ClangBackEnd::SourceLocationContainer &location) + QString::number(location.column()); } -QString clickableLocation(const QString &mainFilePath, - const ClangBackEnd::SourceLocationContainer &location) +void openEditorAt(const ClangBackEnd::DiagnosticContainer &diagnostic) { - const QString filePrefix = fileNamePrefix(mainFilePath, location); - const QString lineColumn = locationToString(location); - const QString linkText = filePrefix + lineColumn; + const ClangBackEnd::SourceLocationContainer location = diagnostic.location(); - return wrapInLink(linkText, QLatin1String(LINK_ACTION_GOTO_LOCATION)); -} - -QString clickableFixIt(const QString &text, bool hasFixIt) -{ - if (!hasFixIt) - return text; - - QString clickableText = text; - QString nonClickableCategory; - const int colonPosition = text.indexOf(QStringLiteral(": ")); - - if (colonPosition != -1) { - nonClickableCategory = text.mid(0, colonPosition + 2); - clickableText = text.mid(colonPosition + 2); - } - - return nonClickableCategory + wrapInLink(clickableText, QLatin1String(LINK_ACTION_APPLY_FIX)); -} - -void openEditorAt(const ClangBackEnd::SourceLocationContainer &location) -{ Core::EditorManager::openEditorAt(location.filePath().toString(), int(location.line()), int(location.column() - 1)); } -void applyFixit(const QVector &fixits) +void applyFixit(const ClangBackEnd::DiagnosticContainer &diagnostic) { - ClangCodeModel::ClangFixItOperation operation(Utf8String(), fixits); + ClangCodeModel::ClangFixItOperation operation(Utf8String(), diagnostic.fixIts()); operation.perform(); } -template -LayoutType *createLayout() +class WidgetFromDiagnostics { - auto *layout = new LayoutType; - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(2); - - return layout; -} - -enum IndentType { IndentDiagnostic, DoNotIndentDiagnostic }; - -QWidget *createDiagnosticLabel(const ClangBackEnd::DiagnosticContainer &diagnostic, - const QString &mainFilePath, - IndentType indentType = DoNotIndentDiagnostic, - bool enableClickableFixits = true) -{ - const bool hasFixit = enableClickableFixits ? !diagnostic.fixIts().isEmpty() : false; - const QString diagnosticText = diagnostic.text().toString().toHtmlEscaped(); - const QString text = clickableLocation(mainFilePath, diagnostic.location()) - + QStringLiteral(": ") - + clickableFixIt(diagnosticText, hasFixit); - const ClangBackEnd::SourceLocationContainer location = diagnostic.location(); - const QVector fixits = diagnostic.fixIts(); - - auto *label = new QLabel(text); - if (indentType == IndentDiagnostic) - label->setContentsMargins(childIndentationOnTheLeftInPixel, 0, 0, 0); - label->setTextFormat(Qt::RichText); - QObject::connect(label, &QLabel::linkActivated, [location, fixits](const QString &action) { - if (action == QLatin1String(LINK_ACTION_APPLY_FIX)) - applyFixit(fixits); - else - openEditorAt(location); - - Utils::ToolTip::hideImmediately(); - }); - - return label; -} - -class MainDiagnosticWidget : public QWidget -{ - Q_OBJECT public: - MainDiagnosticWidget(const ClangBackEnd::DiagnosticContainer &diagnostic, - const ClangCodeModel::Internal::DisplayHints &displayHints) + struct DisplayHints { + bool showCategoryAndEnableOption; + bool showFileNameInMainDiagnostic; + bool enableClickableFixits; + bool limitWidth; + bool hideTooltipAfterLinkActivation; + }; + + static QWidget *create(const QVector &diagnostics, + const DisplayHints &displayHints) { - setContentsMargins(0, 0, 0, 0); - auto *mainLayout = createLayout(); + WidgetFromDiagnostics converter(displayHints); + return converter.createWidget(diagnostics); + } - const ClangBackEnd::SourceLocationContainer location = diagnostic.location(); +private: + enum class IndentMode { Indent, DoNotIndent }; - // Set up header row: category + responsible option - if (displayHints.showMainDiagnosticHeader) { - const QString category = diagnostic.category(); - const QString responsibleOption = diagnostic.enableOption(); + WidgetFromDiagnostics(const DisplayHints &displayHints) + : m_displayHints(displayHints) + { + } - auto *headerLayout = createLayout(); - headerLayout->addWidget(new QLabel(wrapInBoldTags(category)), 1); + QWidget *createWidget(const QVector &diagnostics) + { + const QString text = htmlText(diagnostics); - auto *responsibleOptionLabel = new QLabel(wrapInColor(responsibleOption, "gray")); - headerLayout->addWidget(responsibleOptionLabel, 0); - mainLayout->addLayout(headerLayout); + auto *label = new QLabel; + label->setTextFormat(Qt::RichText); + label->setText(text); + label->setTextInteractionFlags(Qt::TextBrowserInteraction); + // Using "setWordWrap(true)" alone will wrap the text already for small + // widths, so do not require word wrapping until we hit limits. + if (m_displayHints.limitWidth && label->sizeHint().width() > widthLimit()) { + label->setMaximumWidth(widthLimit()); + label->setWordWrap(true); } - // Set up main row: diagnostic text - const Utf8String mainFilePath = displayHints.showFileNameInMainDiagnostic + const TargetIdToDiagnosticTable table = m_targetIdsToDiagnostics; + const bool hideToolTipAfterLinkActivation = m_displayHints.hideTooltipAfterLinkActivation; + QObject::connect(label, &QLabel::linkActivated, [table, hideToolTipAfterLinkActivation] + (const QString &action) { + const ClangBackEnd::DiagnosticContainer diagnostic = table.value(action); + QTC_ASSERT(diagnostic != ClangBackEnd::DiagnosticContainer(), return); + + if (action.startsWith(LINK_ACTION_APPLY_FIX)) + applyFixit(diagnostic); + else + openEditorAt(diagnostic); + + if (hideToolTipAfterLinkActivation) + Utils::ToolTip::hideImmediately(); + }); + + return label; + } + + QString htmlText(const QVector &diagnostics) + { + // For debugging, add: style='border-width:1px;border-color:black' + QString text = ""; + + foreach (const ClangBackEnd::DiagnosticContainer &diagnostic, diagnostics) + text.append(tableRows(diagnostic)); + + text.append("
"); + + return text; + } + + QString tableRows(const ClangBackEnd::DiagnosticContainer &diagnostic) + { + m_mainFilePath = m_displayHints.showFileNameInMainDiagnostic ? Utf8String() - : location.filePath(); - mainLayout->addWidget(createDiagnosticLabel(diagnostic, - mainFilePath, - DoNotIndentDiagnostic, - displayHints.enableClickableFixits)); + : diagnostic.location().filePath(); - setLayout(mainLayout); + QString text; + + if (m_displayHints.showCategoryAndEnableOption) + text.append(diagnosticCategoryAndEnableOptionRow(diagnostic)); + text.append(diagnosticRow(diagnostic, IndentMode::DoNotIndent)); + text.append(diagnosticRowsForChildren(diagnostic)); + + return text; } + + static QString diagnosticCategoryAndEnableOptionRow( + const ClangBackEnd::DiagnosticContainer &diagnostic) + { + const QString text = QString::fromLatin1( + " " + " %1" + " %2" + " ") + .arg(diagnostic.category(), diagnostic.enableOption()); + + return text; + } + + QString diagnosticText(const ClangBackEnd::DiagnosticContainer &diagnostic) + { + const bool hasFixit = m_displayHints.enableClickableFixits + && !diagnostic.fixIts().isEmpty(); + const QString diagnosticText = diagnostic.text().toString().toHtmlEscaped(); + + // For debugging, add to : style='border-width:1px;border-color:red' + const QString text = QString::fromLatin1( + "
" + " " + " " + " " + " " + "
%1: %2
") + .arg(clickableLocation(diagnostic, m_mainFilePath), + clickableFixIt(diagnostic, diagnosticText, hasFixit)); + + return text; + } + + QString diagnosticRow(const ClangBackEnd::DiagnosticContainer &diagnostic, + IndentMode indentMode) + { + const QString text = QString::fromLatin1( + " " + " %2" + " ") + .arg(indentModeToHtmlStyle(indentMode), + diagnosticText(diagnostic)); + + return text; + } + + QString diagnosticRowsForChildren(const ClangBackEnd::DiagnosticContainer &diagnostic) + { + const QVector children = diagnostic.children(); + QString text; + + if (children.size() <= 10) { + text += diagnosticRowsForChildren(children.begin(), children.end()); + } else { + text += diagnosticRowsForChildren(children.begin(), children.begin() + 7); + text += ellipsisRow(); + text += diagnosticRowsForChildren(children.end() - 3, children.end()); + } + + return text; + } + + QString diagnosticRowsForChildren( + const QVector::const_iterator first, + const QVector::const_iterator last) + { + QString text; + + for (auto it = first; it != last; ++it) + text.append(diagnosticRow(*it, IndentMode::Indent)); + + return text; + } + + QString clickableLocation(const ClangBackEnd::DiagnosticContainer &diagnostic, + const QString &mainFilePath) + { + const ClangBackEnd::SourceLocationContainer location = diagnostic.location(); + + const QString filePrefix = fileNamePrefix(mainFilePath, location); + const QString lineColumn = locationToString(location); + const QString linkText = filePrefix + lineColumn; + const QString targetId = generateTargetId(LINK_ACTION_GOTO_LOCATION, diagnostic); + + return wrapInLink(linkText, targetId); + } + + QString clickableFixIt(const ClangBackEnd::DiagnosticContainer &diagnostic, + const QString &text, + bool hasFixIt) + { + if (!hasFixIt) + return text; + + QString clickableText = text; + QString nonClickableCategory; + const int colonPosition = text.indexOf(QStringLiteral(": ")); + + if (colonPosition != -1) { + nonClickableCategory = text.mid(0, colonPosition + 2); + clickableText = text.mid(colonPosition + 2); + } + + const QString targetId = generateTargetId(LINK_ACTION_APPLY_FIX, diagnostic); + + return nonClickableCategory + wrapInLink(clickableText, targetId); + } + + QString generateTargetId(const QString &targetPrefix, + const ClangBackEnd::DiagnosticContainer &diagnostic) + { + const QString idAsString = QString::number(++m_targetIdCounter); + const QString targetId = targetPrefix + idAsString; + m_targetIdsToDiagnostics.insert(targetId, diagnostic); + + return targetId; + } + + static QString wrapInLink(const QString &text, const QString &target) + { + return QStringLiteral("%2").arg(target, text); + } + + static QString ellipsisRow() + { + return QString::fromLatin1( + " " + " ..." + " ") + .arg(indentModeToHtmlStyle(IndentMode::Indent)); + } + + static QString indentModeToHtmlStyle(IndentMode indentMode) + { + return indentMode == IndentMode::Indent + ? QString("padding-left:10px") + : QString(); + } + + static int widthLimit() + { + return QApplication::desktop()->availableGeometry(QCursor::pos()).width() / 2; + } + +private: + const DisplayHints m_displayHints; + + using TargetIdToDiagnosticTable = QHash; + TargetIdToDiagnosticTable m_targetIdsToDiagnostics; + unsigned m_targetIdCounter = 0; + + QString m_mainFilePath; }; -void addChildrenToLayout(const QString &mainFilePath, - const QVector::const_iterator first, - const QVector::const_iterator last, - bool enableClickableFixits, - QLayout &boxLayout) -{ - for (auto it = first; it != last; ++it) { - boxLayout.addWidget(createDiagnosticLabel(*it, - mainFilePath, - IndentDiagnostic, - enableClickableFixits)); - } -} - -void setupChildDiagnostics(const QString &mainFilePath, - const QVector &diagnostics, - bool enableClickableFixits, - QLayout &boxLayout) -{ - if (diagnostics.size() <= 10) { - addChildrenToLayout(mainFilePath, diagnostics.begin(), diagnostics.end(), - enableClickableFixits, boxLayout); - } else { - addChildrenToLayout(mainFilePath, diagnostics.begin(), diagnostics.begin() + 7, - enableClickableFixits, boxLayout); - - auto ellipsisLabel = new QLabel(QStringLiteral("...")); - ellipsisLabel->setContentsMargins(childIndentationOnTheLeftInPixel, 0, 0, 0); - boxLayout.addWidget(ellipsisLabel); - - addChildrenToLayout(mainFilePath, diagnostics.end() - 3, diagnostics.end(), - enableClickableFixits, boxLayout); - } -} - } // anonymous namespace namespace ClangCodeModel { namespace Internal { -void addToolTipToLayout(const ClangBackEnd::DiagnosticContainer &diagnostic, - QLayout *target, - const DisplayHints &displayHints) +QWidget *ClangDiagnosticWidget::create( + const QVector &diagnostics, + const Destination &destination) { - // Set up header and text row for main diagnostic - target->addWidget(new MainDiagnosticWidget(diagnostic, displayHints)); + WidgetFromDiagnostics::DisplayHints hints; - // Set up child rows for notes - setupChildDiagnostics(diagnostic.location().filePath(), - diagnostic.children(), - displayHints.enableClickableFixits, - *target); + if (destination == ToolTip) { + hints.showCategoryAndEnableOption = true; + hints.showFileNameInMainDiagnostic = false; + hints.enableClickableFixits = true; + hints.limitWidth = true; + hints.hideTooltipAfterLinkActivation = true; + } else { // Info Bar + hints.showCategoryAndEnableOption = false; + hints.showFileNameInMainDiagnostic = true; + // Clickable fixits might change toolchain headers, so disable. + hints.enableClickableFixits = false; + hints.limitWidth = false; + hints.hideTooltipAfterLinkActivation = false; + } + + return WidgetFromDiagnostics::create(diagnostics, hints); } } // namespace Internal } // namespace ClangCodeModel - -#include "clangdiagnostictooltipwidget.moc" diff --git a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.h b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.h index 839952fc774..3ea1bfb0ada 100644 --- a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.h +++ b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.h @@ -34,15 +34,13 @@ QT_END_NAMESPACE namespace ClangCodeModel { namespace Internal { -struct DisplayHints { - bool showMainDiagnosticHeader = true; - bool showFileNameInMainDiagnostic = false; - bool enableClickableFixits = true; -}; +class ClangDiagnosticWidget { +public: + enum Destination { ToolTip, InfoBar }; -void addToolTipToLayout(const ClangBackEnd::DiagnosticContainer &diagnostic, - QLayout *target, - const DisplayHints &displayHints = DisplayHints()); + static QWidget *create(const QVector &diagnostics, + const Destination &destination); +}; } // namespace Internal } // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp index 09a7b76b6f1..82f06b59949 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp @@ -263,8 +263,12 @@ void ClangEditorDocumentProcessor::addDiagnosticToolTipToLayout(uint line, uint column, QLayout *target) const { - foreach (const auto &diagnostic, m_diagnosticManager.diagnosticsAt(line, column)) - addToolTipToLayout(diagnostic, target); + using Internal::ClangDiagnosticWidget; + + const QVector diagnostics + = m_diagnosticManager.diagnosticsAt(line, column); + + target->addWidget(ClangDiagnosticWidget::create(diagnostics, ClangDiagnosticWidget::ToolTip)); } void ClangEditorDocumentProcessor::editorDocumentTimerRestarted() @@ -342,16 +346,6 @@ void ClangEditorDocumentProcessor::requestDocumentAnnotations(const QString &pro m_ipcCommunicator.requestDocumentAnnotations(fileContainer); } -static Internal::DisplayHints displayHintsForInfoBar() -{ - Internal::DisplayHints displayHints; - displayHints.showMainDiagnosticHeader = false; - displayHints.showFileNameInMainDiagnostic = true; - displayHints.enableClickableFixits = false; // Tool chain headers might be changed, so disable. - - return displayHints; -} - CppTools::BaseEditorDocumentProcessor::HeaderErrorDiagnosticWidgetCreator ClangEditorDocumentProcessor::creatorForHeaderErrorDiagnosticWidget( const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic) @@ -365,7 +359,8 @@ ClangEditorDocumentProcessor::creatorForHeaderErrorDiagnosticWidget( vbox->setContentsMargins(10, 0, 0, 2); vbox->setSpacing(2); - addToolTipToLayout(firstHeaderErrorDiagnostic, vbox, displayHintsForInfoBar()); + vbox->addWidget(ClangDiagnosticWidget::create({firstHeaderErrorDiagnostic}, + ClangDiagnosticWidget::InfoBar)); auto widget = new QWidget; widget->setLayout(vbox); diff --git a/src/plugins/clangcodemodel/clangtextmark.cpp b/src/plugins/clangcodemodel/clangtextmark.cpp index 8057d66cb61..fede73fde3f 100644 --- a/src/plugins/clangcodemodel/clangtextmark.cpp +++ b/src/plugins/clangcodemodel/clangtextmark.cpp @@ -31,6 +31,7 @@ #include #include +#include #include namespace ClangCodeModel { @@ -84,7 +85,11 @@ void ClangTextMark::setIcon(ClangBackEnd::DiagnosticSeverity severity) bool ClangTextMark::addToolTipContent(QLayout *target) { - Internal::addToolTipToLayout(m_diagnostic, target, Internal::DisplayHints()); + using Internal::ClangDiagnosticWidget; + + QWidget *widget = ClangDiagnosticWidget::create({m_diagnostic}, ClangDiagnosticWidget::ToolTip); + target->addWidget(widget); + return true; } diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp index ae1e74020e6..1d704c8a88b 100644 --- a/src/plugins/clangcodemodel/clangutils.cpp +++ b/src/plugins/clangcodemodel/clangutils.cpp @@ -89,11 +89,14 @@ public: LibClangOptionsBuilder optionsBuilder(*projectPart.data()); + optionsBuilder.addWordWidth(); optionsBuilder.addTargetTriple(); optionsBuilder.addLanguageOption(fileKind); optionsBuilder.addOptionsForLanguage(/*checkForBorlandExtensions*/ true); optionsBuilder.enableExceptions(); + optionsBuilder.addDefineToAvoidIncludingGccOrMinGwIntrinsics(); + optionsBuilder.addDefineFloat128ForMingw(); optionsBuilder.addToolchainAndProjectDefines(); optionsBuilder.undefineCppLanguageFeatureMacrosForMsvc2015(); diff --git a/src/plugins/clangrefactoring/refactoringcompileroptionsbuilder.cpp b/src/plugins/clangrefactoring/refactoringcompileroptionsbuilder.cpp index 372cf1af150..5fa28940f5a 100644 --- a/src/plugins/clangrefactoring/refactoringcompileroptionsbuilder.cpp +++ b/src/plugins/clangrefactoring/refactoringcompileroptionsbuilder.cpp @@ -124,11 +124,14 @@ Utils::SmallStringVector RefactoringCompilerOptionsBuilder::build(CppTools::Proj RefactoringCompilerOptionsBuilder optionsBuilder(projectPart); + optionsBuilder.addWordWidth(); optionsBuilder.addTargetTriple(); optionsBuilder.addLanguageOption(fileKind); optionsBuilder.addOptionsForLanguage(/*checkForBorlandExtensions*/ true); optionsBuilder.enableExceptions(); + optionsBuilder.addDefineFloat128ForMingw(); + optionsBuilder.addDefineToAvoidIncludingGccOrMinGwIntrinsics(); optionsBuilder.addToolchainAndProjectDefines(); optionsBuilder.undefineCppLanguageFeatureMacrosForMsvc2015(); diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerpreconfiguredsessiontests.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerpreconfiguredsessiontests.cpp index eacaa7f4acd..462e26fe836 100644 --- a/src/plugins/clangstaticanalyzer/clangstaticanalyzerpreconfiguredsessiontests.cpp +++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerpreconfiguredsessiontests.cpp @@ -197,17 +197,19 @@ bool ClangStaticAnalyzerPreconfiguredSessionTests::switchToProjectAndTarget(Proj if (project == activeProject && target == activeProject->activeTarget()) return true; // OK, desired project/target already active. - QSignalSpy waitUntilProjectUpdated(CppModelManager::instance(), - &CppModelManager::projectPartsUpdated); - if (project != activeProject) m_sessionManager.setStartupProject(project); - m_sessionManager.setActiveTarget(project, target, ProjectExplorer::SetActive::NoCascade); - const bool waitResult = waitUntilProjectUpdated.wait(30000); - if (!waitResult) { - qWarning() << "waitUntilProjectUpdated() failed"; - return false; + if (target != project->activeTarget()) { + QSignalSpy waitUntilProjectUpdated(CppModelManager::instance(), + &CppModelManager::projectPartsUpdated); + m_sessionManager.setActiveTarget(project, target, ProjectExplorer::SetActive::NoCascade); + + const bool waitResult = waitUntilProjectUpdated.wait(30000); + if (!waitResult) { + qWarning() << "waitUntilProjectUpdated() failed"; + return false; + } } return true; diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp index 75f4f025736..10cb6115ba5 100644 --- a/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp +++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp @@ -86,18 +86,18 @@ ClangStaticAnalyzerRunControl::ClangStaticAnalyzerRunControl( ToolChain *toolChain = ToolChainKitInformation::toolChain(target->kit(), ToolChain::Language::Cxx); QTC_ASSERT(toolChain, return); - m_extraToolChainInfo.wordWidth = toolChain->targetAbi().wordWidth(); - m_extraToolChainInfo.targetTriple = toolChain->originalTargetTriple(); + m_targetTriple = toolChain->originalTargetTriple(); } -static void prependWordWidthArgumentIfNotIncluded(QStringList *arguments, unsigned char wordWidth) +static void prependWordWidthArgumentIfNotIncluded(QStringList *arguments, + ProjectPart::ToolChainWordWidth wordWidth) { QTC_ASSERT(arguments, return); const QString m64Argument = QLatin1String("-m64"); const QString m32Argument = QLatin1String("-m32"); - const QString argument = wordWidth == 64 ? m64Argument : m32Argument; + const QString argument = wordWidth == ProjectPart::WordWidth64Bit ? m64Argument : m32Argument; if (!arguments->contains(argument)) arguments->prepend(argument); @@ -165,25 +165,19 @@ class ClangStaticAnalyzerOptionsBuilder : public CompilerOptionsBuilder { public: static QStringList build(const CppTools::ProjectPart &projectPart, - CppTools::ProjectFile::Kind fileKind, - const ExtraToolChainInfo &extraParams) + CppTools::ProjectFile::Kind fileKind) { ClangStaticAnalyzerOptionsBuilder optionsBuilder(projectPart); + optionsBuilder.addWordWidth(); optionsBuilder.addTargetTriple(); optionsBuilder.addLanguageOption(fileKind); optionsBuilder.addOptionsForLanguage(false); optionsBuilder.enableExceptions(); - // In gcc headers, lots of built-ins are referenced that clang does not understand. - // Therefore, prevent the inclusion of the header that references them. Of course, this - // will break if code actually requires stuff from there, but that should be the less common - // case. + optionsBuilder.addDefineFloat128ForMingw(); + optionsBuilder.addDefineToAvoidIncludingGccOrMinGwIntrinsics(); const Core::Id type = projectPart.toolchainType; - if (type == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID - || type == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID) - optionsBuilder.addDefine("#define _X86INTRIN_H_INCLUDED"); - if (type != ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) optionsBuilder.addDefines(projectPart.toolchainDefines); optionsBuilder.addDefines(projectPart.projectDefines); @@ -195,10 +189,7 @@ public: if (type != ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) optionsBuilder.add(QLatin1String("-fPIC")); // TODO: Remove? - QStringList options = optionsBuilder.options(); - prependWordWidthArgumentIfNotIncluded(&options, extraParams.wordWidth); - - return options; + return optionsBuilder.options(); } ClangStaticAnalyzerOptionsBuilder(const CppTools::ProjectPart &projectPart) @@ -263,6 +254,13 @@ private: return CompilerOptionsBuilder::defineOption(); } + QString undefineOption() const override + { + if (m_isMsvcToolchain) + return QLatin1String("/U"); + return CompilerOptionsBuilder::undefineOption(); + } + void enableExceptions() override { if (m_isMsvcToolchain) @@ -318,11 +316,11 @@ static QStringList createHeaderPathsOptionsForClangOnMac(const ProjectPart &proj static QStringList tweakedArguments(const ProjectPart &projectPart, const QString &filePath, const QStringList &arguments, - const ExtraToolChainInfo &extraParams) + const QString &targetTriple) { QStringList newArguments = inputAndOutputArgumentsRemoved(filePath, arguments); - prependWordWidthArgumentIfNotIncluded(&newArguments, extraParams.wordWidth); - prependTargetTripleIfNotIncludedAndNotEmpty(&newArguments, extraParams.targetTriple); + prependWordWidthArgumentIfNotIncluded(&newArguments, projectPart.toolChainWordWidth); + prependTargetTripleIfNotIncludedAndNotEmpty(&newArguments, targetTriple); newArguments.append(createHeaderPathsOptionsForClangOnMac(projectPart)); newArguments.append(createMsCompatibilityVersionOption(projectPart)); newArguments.append(createOptionsToUndefineClangVersionMacrosForMsvc(projectPart)); @@ -334,7 +332,7 @@ static QStringList tweakedArguments(const ProjectPart &projectPart, static AnalyzeUnits unitsToAnalyzeFromCompilerCallData( const QHash &projectFileToProjectPart, const ProjectInfo::CompilerCallData &compilerCallData, - const ExtraToolChainInfo &extraParams) + const QString &targetTriple) { qCDebug(LOG) << "Taking arguments for analyzing from CompilerCallData."; @@ -354,7 +352,7 @@ static AnalyzeUnits unitsToAnalyzeFromCompilerCallData( const QStringList arguments = tweakedArguments(*projectPart, file, options, - extraParams); + targetTriple); unitsToAnalyze << AnalyzeUnit(file, arguments); } } @@ -363,8 +361,7 @@ static AnalyzeUnits unitsToAnalyzeFromCompilerCallData( return unitsToAnalyze; } -static AnalyzeUnits unitsToAnalyzeFromProjectParts(const QList projectParts, - const ExtraToolChainInfo &extraParams) +static AnalyzeUnits unitsToAnalyzeFromProjectParts(const QList projectParts) { qCDebug(LOG) << "Taking arguments for analyzing from ProjectParts."; @@ -380,9 +377,7 @@ static AnalyzeUnits unitsToAnalyzeFromProjectParts(const QList QTC_CHECK(file.kind != ProjectFile::Unclassified); if (ProjectFile::isSource(file.kind)) { const QStringList arguments - = ClangStaticAnalyzerOptionsBuilder::build(*projectPart.data(), - file.kind, - extraParams); + = ClangStaticAnalyzerOptionsBuilder::build(*projectPart.data(), file.kind); unitsToAnalyze << AnalyzeUnit(file.path, arguments); } } @@ -411,13 +406,13 @@ AnalyzeUnits ClangStaticAnalyzerRunControl::sortedUnitsToAnalyze() AnalyzeUnits units; const ProjectInfo::CompilerCallData compilerCallData = m_projectInfo.compilerCallData(); if (compilerCallData.isEmpty()) { - units = unitsToAnalyzeFromProjectParts(m_projectInfo.projectParts(), m_extraToolChainInfo); + units = unitsToAnalyzeFromProjectParts(m_projectInfo.projectParts()); } else { const QHash projectFileToProjectPart = generateProjectFileToProjectPartMapping(m_projectInfo.projectParts()); units = unitsToAnalyzeFromCompilerCallData(projectFileToProjectPart, compilerCallData, - m_extraToolChainInfo); + m_targetTriple); } Utils::sort(units, &AnalyzeUnit::file); diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h index 18deebfcbd3..ce974bac37f 100644 --- a/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h +++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h @@ -47,11 +47,6 @@ struct AnalyzeUnit { }; typedef QList AnalyzeUnits; -struct ExtraToolChainInfo { - unsigned char wordWidth = 0; - QString targetTriple; -}; - class ClangStaticAnalyzerRunControl : public ProjectExplorer::RunControl { Q_OBJECT @@ -88,7 +83,7 @@ private: private: const CppTools::ProjectInfo m_projectInfo; - ExtraToolChainInfo m_extraToolChainInfo; + QString m_targetTriple; Utils::Environment m_environment; QString m_clangExecutable; diff --git a/src/plugins/cmakeprojectmanager/cmakekitconfigwidget.h b/src/plugins/cmakeprojectmanager/cmakekitconfigwidget.h index 48ccf1673a8..3839750f664 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitconfigwidget.h +++ b/src/plugins/cmakeprojectmanager/cmakekitconfigwidget.h @@ -29,6 +29,7 @@ QT_BEGIN_NAMESPACE class QComboBox; +class QDialog; class QLabel; class QPlainTextEdit; class QPushButton; diff --git a/src/plugins/coreplugin/coreplugin.pro b/src/plugins/coreplugin/coreplugin.pro index 9c8be5cfa76..6de7837acfc 100644 --- a/src/plugins/coreplugin/coreplugin.pro +++ b/src/plugins/coreplugin/coreplugin.pro @@ -218,7 +218,8 @@ HEADERS += corejsextensions.h \ iwelcomepage.h \ systemsettings.h \ coreicons.h \ - editormanager/documentmodel_p.h + editormanager/documentmodel_p.h \ + diffservice.h FORMS += dialogs/newdialog.ui \ dialogs/saveitemsdialog.ui \ diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs index ed50f54985d..fe6b3cb75e9 100644 --- a/src/plugins/coreplugin/coreplugin.qbs +++ b/src/plugins/coreplugin/coreplugin.qbs @@ -41,6 +41,7 @@ Project { "corejsextensions.cpp", "corejsextensions.h", "coreplugin.cpp", "coreplugin.h", "designmode.cpp", "designmode.h", + "diffservice.h", "documentmanager.cpp", "documentmanager.h", "editmode.cpp", "editmode.h", "editortoolbar.cpp", "editortoolbar.h", diff --git a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp index 885b08e9346..a36ecd9d485 100644 --- a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp +++ b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp @@ -25,12 +25,15 @@ #include "saveitemsdialog.h" +#include #include #include #include #include +#include + #include #include #include @@ -51,6 +54,12 @@ SaveItemsDialog::SaveItemsDialog(QWidget *parent, // QDialogButtonBox's behavior for "destructive" is wrong, the "do not save" should be left-aligned const QDialogButtonBox::ButtonRole discardButtonRole = Utils::HostOsInfo::isMacHost() ? QDialogButtonBox::ResetRole : QDialogButtonBox::DestructiveRole; + + if (ExtensionSystem::PluginManager::getObject()) { + m_diffButton = m_ui.buttonBox->addButton(tr("&Diff"), discardButtonRole); + connect(m_diffButton, &QAbstractButton::clicked, this, &SaveItemsDialog::collectFilesToDiff); + } + QPushButton *discardButton = m_ui.buttonBox->addButton(tr("Do not Save"), discardButtonRole); m_ui.buttonBox->button(QDialogButtonBox::Save)->setDefault(true); m_ui.treeWidget->setFocus(); @@ -80,13 +89,13 @@ SaveItemsDialog::SaveItemsDialog(QWidget *parent, if (Utils::HostOsInfo::isMacHost()) m_ui.treeWidget->setAlternatingRowColors(true); adjustButtonWidths(); - updateSaveButton(); + updateButtons(); connect(m_ui.buttonBox->button(QDialogButtonBox::Save), &QAbstractButton::clicked, this, &SaveItemsDialog::collectItemsToSave); connect(discardButton, &QAbstractButton::clicked, this, &SaveItemsDialog::discardAll); connect(m_ui.treeWidget, &QTreeWidget::itemSelectionChanged, - this, &SaveItemsDialog::updateSaveButton); + this, &SaveItemsDialog::updateButtons); } void SaveItemsDialog::setMessage(const QString &msg) @@ -94,19 +103,27 @@ void SaveItemsDialog::setMessage(const QString &msg) m_ui.msgLabel->setText(msg); } -void SaveItemsDialog::updateSaveButton() +void SaveItemsDialog::updateButtons() { int count = m_ui.treeWidget->selectedItems().count(); - QPushButton *button = m_ui.buttonBox->button(QDialogButtonBox::Save); + QPushButton *saveButton = m_ui.buttonBox->button(QDialogButtonBox::Save); + bool buttonsEnabled = true; + QString saveText = tr("Save"); + QString diffText = tr("&Diff && Cancel"); if (count == m_ui.treeWidget->topLevelItemCount()) { - button->setEnabled(true); - button->setText(tr("Save All")); + saveText = tr("Save All"); + diffText = tr("&Diff All && Cancel"); } else if (count == 0) { - button->setEnabled(false); - button->setText(tr("Save")); + buttonsEnabled = false; } else { - button->setEnabled(true); - button->setText(tr("Save Selected")); + saveText = tr("Save Selected"); + diffText = tr("&Diff Selected && Cancel"); + } + saveButton->setEnabled(buttonsEnabled); + saveButton->setText(saveText); + if (m_diffButton) { + m_diffButton->setEnabled(buttonsEnabled); + m_diffButton->setText(diffText); } } @@ -145,6 +162,16 @@ void SaveItemsDialog::collectItemsToSave() accept(); } +void SaveItemsDialog::collectFilesToDiff() +{ + m_filesToDiff.clear(); + foreach (QTreeWidgetItem *item, m_ui.treeWidget->selectedItems()) { + if (IDocument *doc = item->data(0, Qt::UserRole).value()) + m_filesToDiff.append(doc->filePath().toString()); + } + reject(); +} + void SaveItemsDialog::discardAll() { m_ui.treeWidget->clearSelection(); @@ -156,6 +183,11 @@ QList SaveItemsDialog::itemsToSave() const return m_itemsToSave; } +QStringList SaveItemsDialog::filesToDiff() const +{ + return m_filesToDiff; +} + void SaveItemsDialog::setAlwaysSaveMessage(const QString &msg) { m_ui.saveBeforeBuildCheckBox->setText(msg); diff --git a/src/plugins/coreplugin/dialogs/saveitemsdialog.h b/src/plugins/coreplugin/dialogs/saveitemsdialog.h index 623cb2e85f7..1048ca03481 100644 --- a/src/plugins/coreplugin/dialogs/saveitemsdialog.h +++ b/src/plugins/coreplugin/dialogs/saveitemsdialog.h @@ -54,15 +54,19 @@ public: void setAlwaysSaveMessage(const QString &msg); bool alwaysSaveChecked(); QList itemsToSave() const; + QStringList filesToDiff() const; private: void collectItemsToSave(); + void collectFilesToDiff(); void discardAll(); - void updateSaveButton(); + void updateButtons(); void adjustButtonWidths(); Ui::SaveItemsDialog m_ui; QList m_itemsToSave; + QStringList m_filesToDiff; + QPushButton *m_diffButton = nullptr; }; } // namespace Internal diff --git a/src/plugins/coreplugin/diffservice.h b/src/plugins/coreplugin/diffservice.h new file mode 100644 index 00000000000..3b282170ddc --- /dev/null +++ b/src/plugins/coreplugin/diffservice.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "core_global.h" +#include + +QT_FORWARD_DECLARE_CLASS(QStringList) + +namespace Core { + +class CORE_EXPORT DiffService +{ +public: + virtual ~DiffService() {} + + virtual void diffModifiedFiles(const QStringList &fileNames) = 0; +}; + +} // namespace Core + +QT_BEGIN_NAMESPACE +Q_DECLARE_INTERFACE(Core::DiffService, "Core::DiffService") +QT_END_NAMESPACE diff --git a/src/plugins/coreplugin/documentmanager.cpp b/src/plugins/coreplugin/documentmanager.cpp index 2553b96e612..5a936731fa0 100644 --- a/src/plugins/coreplugin/documentmanager.cpp +++ b/src/plugins/coreplugin/documentmanager.cpp @@ -29,6 +29,7 @@ #include "idocument.h" #include "coreconstants.h" +#include #include #include #include @@ -38,6 +39,8 @@ #include #include +#include + #include #include #include @@ -606,6 +609,11 @@ static bool saveModifiedFilesHelper(const QList &documents, (*alwaysSave) = dia.alwaysSaveChecked(); if (failedToSave) (*failedToSave) = modifiedDocuments; + const QStringList filesToDiff = dia.filesToDiff(); + if (!filesToDiff.isEmpty()) { + if (auto diffService = ExtensionSystem::PluginManager::getObject()) + diffService->diffModifiedFiles(filesToDiff); + } return false; } if (alwaysSave) @@ -978,6 +986,7 @@ void DocumentManager::checkForReload() // handle the IDocuments QStringList errorStrings; + QStringList filesToDiff; foreach (IDocument *document, changedIDocuments) { IDocument::ChangeTrigger trigger = IDocument::TriggerInternal; IDocument::ChangeType type = IDocument::TypePermissions; @@ -1052,7 +1061,6 @@ void DocumentManager::checkForReload() documentsToClose << document; } else if (defaultBehavior == IDocument::IgnoreAll) { // content change or removed, but settings say ignore - document->setModified(true); success = document->reload(&errorString, IDocument::FlagIgnore, type); // either the default behavior is to always ask, // or the ReloadUnmodified default behavior didn't kick in, @@ -1068,9 +1076,8 @@ void DocumentManager::checkForReload() // IDocument wants us to ask } else if (type == IDocument::TypeContents) { // content change, IDocument wants to ask user - if (previousReloadAnswer == ReloadNone) { + if (previousReloadAnswer == ReloadNone || previousReloadAnswer == ReloadNoneAndDiff) { // answer already given, ignore - document->setModified(true); success = document->reload(&errorString, IDocument::FlagIgnore, IDocument::TypeContents); } else if (previousReloadAnswer == ReloadAll) { // answer already given, reload @@ -1078,7 +1085,8 @@ void DocumentManager::checkForReload() } else { // Ask about content change previousReloadAnswer = reloadPrompt(document->filePath(), document->isModified(), - ICore::dialogParent()); + ExtensionSystem::PluginManager::getObject(), + ICore::dialogParent()); switch (previousReloadAnswer) { case ReloadAll: case ReloadCurrent: @@ -1086,7 +1094,7 @@ void DocumentManager::checkForReload() break; case ReloadSkipCurrent: case ReloadNone: - document->setModified(true); + case ReloadNoneAndDiff: success = document->reload(&errorString, IDocument::FlagIgnore, IDocument::TypeContents); break; case CloseCurrent: @@ -1094,6 +1102,9 @@ void DocumentManager::checkForReload() break; } } + if (previousReloadAnswer == ReloadNoneAndDiff) + filesToDiff.append(document->filePath().toString()); + // IDocument wants us to ask, and it's the TypeRemoved case } else { // Ask about removed file @@ -1137,6 +1148,12 @@ void DocumentManager::checkForReload() d->m_blockedIDocument = 0; } + + if (!filesToDiff.isEmpty()) { + if (auto diffService = ExtensionSystem::PluginManager::getObject()) + diffService->diffModifiedFiles(filesToDiff); + } + if (!errorStrings.isEmpty()) QMessageBox::critical(ICore::dialogParent(), tr("File Error"), errorStrings.join(QLatin1Char('\n'))); diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index 7f344ecd95f..5db180a4473 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -1905,9 +1906,9 @@ void EditorManagerPrivate::handleDocumentStateChange() IDocument *document = qobject_cast(sender()); if (!document->isModified()) document->removeAutoSaveFile(); - if (EditorManager::currentDocument() == document) { + if (EditorManager::currentDocument() == document) emit m_instance->currentDocumentStateChanged(); - } + emit m_instance->documentStateChanged(document); } void EditorManagerPrivate::editorAreaDestroyed(QObject *area) @@ -2168,11 +2169,21 @@ void EditorManagerPrivate::revertToSaved(IDocument *document) QMessageBox::Yes|QMessageBox::No, ICore::mainWindow()); msgBox.button(QMessageBox::Yes)->setText(tr("Proceed")); msgBox.button(QMessageBox::No)->setText(tr("Cancel")); + + QPushButton *diffButton = nullptr; + auto diffService = ExtensionSystem::PluginManager::getObject(); + if (diffService) + diffButton = msgBox.addButton(tr("Cancel && &Diff"), QMessageBox::RejectRole); + msgBox.setDefaultButton(QMessageBox::No); msgBox.setEscapeButton(QMessageBox::No); if (msgBox.exec() == QMessageBox::No) return; + if (diffService && msgBox.clickedButton() == diffButton) { + diffService->diffModifiedFiles(QStringList() << fileName); + return; + } } QString errorString; if (!document->reload(&errorString, IDocument::FlagReload, IDocument::TypeContents)) diff --git a/src/plugins/coreplugin/editormanager/editormanager.h b/src/plugins/coreplugin/editormanager/editormanager.h index 46656e51b0c..b700662b9c7 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.h +++ b/src/plugins/coreplugin/editormanager/editormanager.h @@ -179,6 +179,7 @@ public: // for tests signals: void currentEditorChanged(Core::IEditor *editor); void currentDocumentStateChanged(); + void documentStateChanged(Core::IDocument *document); void editorCreated(Core::IEditor *editor, const QString &fileName); void editorOpened(Core::IEditor *editor); void editorAboutToClose(Core::IEditor *editor); diff --git a/src/plugins/coreplugin/idocument.cpp b/src/plugins/coreplugin/idocument.cpp index 9023eb73eae..fefff6a701b 100644 --- a/src/plugins/coreplugin/idocument.cpp +++ b/src/plugins/coreplugin/idocument.cpp @@ -79,7 +79,6 @@ public: bool hasWriteWarning = false; bool restored = false; bool isSuspendAllowed = false; - bool isModified = false; }; } // namespace Internal @@ -201,17 +200,7 @@ bool IDocument::shouldAutoSave() const bool IDocument::isModified() const { - return d->isModified; -} - -void IDocument::setModified(bool modified) -{ - if (d->isModified == modified) - return; - - d->isModified = modified; - emit modificationChanged(modified); - emit changed(); + return false; } bool IDocument::isSaveAsAllowed() const diff --git a/src/plugins/coreplugin/idocument.h b/src/plugins/coreplugin/idocument.h index 79cc403e6f4..acb1feb58b8 100644 --- a/src/plugins/coreplugin/idocument.h +++ b/src/plugins/coreplugin/idocument.h @@ -106,9 +106,6 @@ public: bool isTemporary() const; void setTemporary(bool temporary); - bool isModified() const; - void setModified(bool modified); - virtual QString fallbackSaveAsPath() const; virtual QString fallbackSaveAsFileName() const; @@ -116,6 +113,7 @@ public: void setMimeType(const QString &mimeType); virtual bool shouldAutoSave() const; + virtual bool isModified() const; virtual bool isSaveAsAllowed() const; bool isSuspendAllowed() const; void setSuspendAllowed(bool value); @@ -138,8 +136,6 @@ signals: // For meta data changes: file name, modified state, ... void changed(); - void modificationChanged(bool modified); - // For changes in the contents of the document void contentsChanged(); diff --git a/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp b/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp index fdb41c40f7a..7ef01db7520 100644 --- a/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp +++ b/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp @@ -200,7 +200,9 @@ QString ResourcePreviewHoverHandler::makeTooltip() const if (mimeType.name().startsWith("image", Qt::CaseInsensitive)) ret += QString("
").arg(m_resPath); - ret += QString("%2").arg(m_resPath).arg(m_resPath); + ret += QString("%2") + .arg(m_resPath) + .arg(QDir::toNativeSeparators(m_resPath)); } return ret; } diff --git a/src/plugins/cpptools/compileroptionsbuilder.cpp b/src/plugins/cpptools/compileroptionsbuilder.cpp index 6f2991fea06..a2b2ed3ba68 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.cpp +++ b/src/plugins/cpptools/compileroptionsbuilder.cpp @@ -87,6 +87,14 @@ void CompilerOptionsBuilder::addDefine(const QByteArray &defineDirective) m_options.append(defineDirectiveToDefineOption(defineDirective)); } +void CompilerOptionsBuilder::addWordWidth() +{ + const QString argument = m_projectPart.toolChainWordWidth == ProjectPart::WordWidth64Bit + ? QLatin1String("-m64") + : QLatin1String("-m32"); + add(argument); +} + void CompilerOptionsBuilder::addTargetTriple() { if (!m_projectPart.targetTriple.isEmpty()) { @@ -262,6 +270,20 @@ void CompilerOptionsBuilder::addOptionsForLanguage(bool checkForBorlandExtension m_options.append(opts); } +void CompilerOptionsBuilder::addDefineToAvoidIncludingGccOrMinGwIntrinsics() +{ + // In gcc headers, lots of built-ins are referenced that clang does not understand. + // Therefore, prevent the inclusion of the header that references them. Of course, this + // will break if code actually requires stuff from there, but that should be the less common + // case. + + const Core::Id type = m_projectPart.toolchainType; + if (type == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID + || type == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID) { + addDefine("#define _X86INTRIN_H_INCLUDED"); + } +} + static QByteArray toMsCompatibilityVersionFormat(const QByteArray &mscFullVer) { return mscFullVer.left(2) @@ -342,10 +364,18 @@ void CompilerOptionsBuilder::undefineCppLanguageFeatureMacrosForMsvc2015() // Undefine the language feature macros that are pre-defined in clang-cl 3.8.0, // but not in MSVC2015's cl.exe. foreach (const QString ¯oName, languageFeatureMacros()) - m_options.append(QLatin1String("/U") + macroName); + m_options.append(undefineOption() + macroName); } } +void CompilerOptionsBuilder::addDefineFloat128ForMingw() +{ + // TODO: Remove once this is fixed in clang >= 3.9. + // https://llvm.org/bugs/show_bug.cgi?id=30685 + if (m_projectPart.toolchainType == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID) + addDefine("#define __float128 void"); +} + QString CompilerOptionsBuilder::includeOption() const { return QLatin1String("-I"); @@ -364,6 +394,11 @@ QString CompilerOptionsBuilder::defineOption() const return QLatin1String("-D"); } +QString CompilerOptionsBuilder::undefineOption() const +{ + return QLatin1String("-U"); +} + static bool isGccOrMinGwToolchain(const Core::Id &toolchainType) { return toolchainType == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID diff --git a/src/plugins/cpptools/compileroptionsbuilder.h b/src/plugins/cpptools/compileroptionsbuilder.h index 34c7103f1b5..ced3c0af1ba 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.h +++ b/src/plugins/cpptools/compileroptionsbuilder.h @@ -44,6 +44,7 @@ public: void addDefine(const QByteArray &defineDirective); // Add options based on project part + void addWordWidth(); virtual void addTargetTriple(); virtual void enableExceptions(); void addHeaderPathOptions(); @@ -52,14 +53,19 @@ public: virtual void addLanguageOption(ProjectFile::Kind fileKind); virtual void addOptionsForLanguage(bool checkForBorlandExtensions = true); + void addDefineToAvoidIncludingGccOrMinGwIntrinsics(); + void addMsvcCompatibilityVersion(); void undefineCppLanguageFeatureMacrosForMsvc2015(); + void addDefineFloat128ForMingw(); + protected: virtual bool excludeDefineDirective(const QByteArray &defineDirective) const; virtual bool excludeHeaderPath(const QString &headerPath) const; virtual QString defineOption() const; + virtual QString undefineOption() const; virtual QString includeOption() const; const ProjectPart m_projectPart; diff --git a/src/plugins/cpptools/projectpart.cpp b/src/plugins/cpptools/projectpart.cpp index 85ea324c9f8..921d981a611 100644 --- a/src/plugins/cpptools/projectpart.cpp +++ b/src/plugins/cpptools/projectpart.cpp @@ -32,6 +32,7 @@ namespace CppTools { ProjectPart::ProjectPart() : project(0) + , toolChainWordWidth(WordWidth32Bit) , isMsvc2015Toolchain(false) , languageVersion(CXX14) , languageExtensions(NoExtensions) diff --git a/src/plugins/cpptools/projectpart.h b/src/plugins/cpptools/projectpart.h index b18a57e1497..ea45720589c 100644 --- a/src/plugins/cpptools/projectpart.h +++ b/src/plugins/cpptools/projectpart.h @@ -81,6 +81,11 @@ public: // Types Qt5 = 2 }; + enum ToolChainWordWidth { + WordWidth32Bit, + WordWidth64Bit, + }; + using Ptr = QSharedPointer; @@ -103,6 +108,7 @@ public: // fields QByteArray projectDefines; QByteArray toolchainDefines; Core::Id toolchainType; + ToolChainWordWidth toolChainWordWidth; bool isMsvc2015Toolchain; QString targetTriple; ProjectPartHeaderPaths headerPaths; diff --git a/src/plugins/cpptools/projectpartbuilder.cpp b/src/plugins/cpptools/projectpartbuilder.cpp index b01795f9cf2..98169f7d230 100644 --- a/src/plugins/cpptools/projectpartbuilder.cpp +++ b/src/plugins/cpptools/projectpartbuilder.cpp @@ -343,6 +343,9 @@ void ProjectPartBuilder::evaluateProjectPartToolchain( projectPart->toolchainType = toolChain->typeId(); projectPart->isMsvc2015Toolchain = toolChain->targetAbi().osFlavor() == ProjectExplorer::Abi::WindowsMsvc2015Flavor; + projectPart->toolChainWordWidth = toolChain->targetAbi().wordWidth() == 64 + ? ProjectPart::WordWidth64Bit + : ProjectPart::WordWidth32Bit; projectPart->targetTriple = targetTriple(projectPart->project, toolChain->typeId()); projectPart->updateLanguageFeatures(); } diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 96727efea1f..15d82c3436b 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -1209,6 +1209,8 @@ void CdbEngine::activateFrame(int index) qPrintable(frame.file), frame.line); stackHandler()->setCurrentIndex(index); gotoLocation(frame); + if (m_pythonVersion > 0x030000) + runCommand({".frame " + QString::number(index), NoFlags}); updateLocals(); } diff --git a/src/plugins/designer/formwindowfile.cpp b/src/plugins/designer/formwindowfile.cpp index a6c58617fc9..e5b6a313bef 100644 --- a/src/plugins/designer/formwindowfile.cpp +++ b/src/plugins/designer/formwindowfile.cpp @@ -59,9 +59,6 @@ FormWindowFile::FormWindowFile(QDesignerFormWindowInterface *form, QObject *pare connect(m_formWindow->commandHistory(), &QUndoStack::indexChanged, this, &FormWindowFile::setShouldAutoSave); connect(m_formWindow.data(), &QDesignerFormWindowInterface::changed, this, &FormWindowFile::updateIsModified); - connect(this, &IDocument::modificationChanged, m_formWindow.data(), &QDesignerFormWindowInterface::setDirty); - - setModified(m_formWindow->isDirty()); m_resourceHandler = new ResourceHandler(form); connect(this, &FormWindowFile::filePathChanged, @@ -186,10 +183,16 @@ void FormWindowFile::setFilePath(const FileName &newName) void FormWindowFile::updateIsModified() { + if (m_modificationChangedGuard.isLocked()) + return; + bool value = m_formWindow && m_formWindow->isDirty(); if (value) emit contentsChanged(); - setModified(value); + if (value == m_isModified) + return; + m_isModified = value; + emit changed(); } bool FormWindowFile::shouldAutoSave() const @@ -197,6 +200,11 @@ bool FormWindowFile::shouldAutoSave() const return m_shouldAutoSave; } +bool FormWindowFile::isModified() const +{ + return m_formWindow && m_formWindow->isDirty(); +} + bool FormWindowFile::isSaveAsAllowed() const { return true; @@ -204,8 +212,20 @@ bool FormWindowFile::isSaveAsAllowed() const bool FormWindowFile::reload(QString *errorString, ReloadFlag flag, ChangeType type) { - if (flag == FlagIgnore) + if (flag == FlagIgnore) { + if (!m_formWindow || type != TypeContents) + return true; + const bool wasModified = m_formWindow->isDirty(); + { + Utils::GuardLocker locker(m_modificationChangedGuard); + // hack to ensure we clean the clear state in form window + m_formWindow->setDirty(false); + m_formWindow->setDirty(true); + } + if (!wasModified) + updateIsModified(); return true; + } if (type == TypePermissions) { emit changed(); } else { diff --git a/src/plugins/designer/formwindowfile.h b/src/plugins/designer/formwindowfile.h index f12e854a2a1..a78d576ad9a 100644 --- a/src/plugins/designer/formwindowfile.h +++ b/src/plugins/designer/formwindowfile.h @@ -26,6 +26,7 @@ #pragma once #include +#include #include @@ -53,6 +54,7 @@ public: QByteArray contents() const override; bool setContents(const QByteArray &contents) override; bool shouldAutoSave() const override; + bool isModified() const override; bool isSaveAsAllowed() const override; bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override; QString fallbackSaveAsFileName() const override; @@ -81,7 +83,9 @@ private: // Might actually go out of scope before the IEditor due // to deleting the WidgetHost which owns it. QPointer m_formWindow; + bool m_isModified = false; ResourceHandler *m_resourceHandler = nullptr; + Utils::Guard m_modificationChangedGuard; }; } // namespace Internal diff --git a/src/plugins/diffeditor/diffeditor.cpp b/src/plugins/diffeditor/diffeditor.cpp index c2d681c13e1..0e9d7fc318d 100644 --- a/src/plugins/diffeditor/diffeditor.cpp +++ b/src/plugins/diffeditor/diffeditor.cpp @@ -69,19 +69,6 @@ static const char useDiffEditorKeyC[] = "UseDiffEditor"; using namespace TextEditor; -namespace { - -class Guard -{ -public: - Guard(int *state) : m_state(state) { ++(*state); } - ~Guard() { --(*m_state); QTC_ASSERT(*m_state >= 0, return); } -private: - int *m_state; -}; - -} // namespace - namespace DiffEditor { namespace Internal { @@ -226,7 +213,6 @@ DiffEditor::DiffEditor() , m_viewSwitcherAction(0) , m_currentViewIndex(-1) , m_currentDiffFileIndex(-1) - , m_ignoreChanges(0) , m_sync(false) , m_showDescription(true) { @@ -329,7 +315,7 @@ void DiffEditor::setDocument(QSharedPointer(doc)) DiffEditor::DiffEditor(DiffEditorDocument *doc) : DiffEditor() { - Guard guard(&m_ignoreChanges); + Utils::GuardLocker guard(m_ignoreChanges); setDocument(QSharedPointer(doc)); setupView(loadSettings()); } @@ -343,7 +329,7 @@ DiffEditor::~DiffEditor() Core::IEditor *DiffEditor::duplicate() { DiffEditor *editor = new DiffEditor(); - Guard guard(&editor->m_ignoreChanges); + Utils::GuardLocker guard(editor->m_ignoreChanges); editor->setDocument(m_document); editor->m_sync = m_sync; @@ -371,7 +357,7 @@ QWidget *DiffEditor::toolBar() void DiffEditor::documentHasChanged() { - Guard guard(&m_ignoreChanges); + Utils::GuardLocker guard(m_ignoreChanges); const QList diffFileList = m_document->diffFiles(); updateDescription(); @@ -430,7 +416,7 @@ void DiffEditor::documentHasChanged() void DiffEditor::toggleDescription() { - if (m_ignoreChanges > 0) + if (m_ignoreChanges.isLocked()) return; m_showDescription = !m_showDescription; @@ -446,7 +432,7 @@ void DiffEditor::updateDescription() m_descriptionWidget->setPlainText(description); m_descriptionWidget->setVisible(m_showDescription && !description.isEmpty()); - Guard guard(&m_ignoreChanges); + Utils::GuardLocker guard(m_ignoreChanges); m_toggleDescriptionAction->setChecked(m_showDescription); m_toggleDescriptionAction->setToolTip(m_showDescription ? tr("Hide Change Description") : tr("Show Change Description")); @@ -458,7 +444,7 @@ void DiffEditor::updateDescription() void DiffEditor::contextLineCountHasChanged(int lines) { QTC_ASSERT(!m_document->isContextLineCountForced(), return); - if (m_ignoreChanges > 0 || lines == m_document->contextLineCount()) + if (m_ignoreChanges.isLocked() || lines == m_document->contextLineCount()) return; m_document->setContextLineCount(lines); @@ -471,7 +457,7 @@ void DiffEditor::ignoreWhitespaceHasChanged() { const bool ignore = m_whitespaceButtonAction->isChecked(); - if (m_ignoreChanges > 0 || ignore == m_document->ignoreWhitespace()) + if (m_ignoreChanges.isLocked() || ignore == m_document->ignoreWhitespace()) return; m_document->setIgnoreWhitespace(ignore); saveSetting(QLatin1String(ignoreWhitespaceKeyC), ignore); @@ -494,7 +480,7 @@ void DiffEditor::prepareForReload() } { - Guard guard(&m_ignoreChanges); + Utils::GuardLocker guard(m_ignoreChanges); m_contextSpinBox->setValue(m_document->contextLineCount()); m_whitespaceButtonAction->setChecked(m_document->ignoreWhitespace()); } @@ -539,12 +525,12 @@ void DiffEditor::updateEntryToolTip() void DiffEditor::setCurrentDiffFileIndex(int index) { - if (m_ignoreChanges > 0) + if (m_ignoreChanges.isLocked()) return; QTC_ASSERT((index < 0) != (m_entriesComboBox->count() > 0), return); - Guard guard(&m_ignoreChanges); + Utils::GuardLocker guard(m_ignoreChanges); m_currentDiffFileIndex = index; currentView()->setCurrentDiffFileIndex(index); @@ -575,7 +561,7 @@ void DiffEditor::updateDiffEditorSwitcher() void DiffEditor::toggleSync() { - if (m_ignoreChanges > 0) + if (m_ignoreChanges.isLocked()) return; QTC_ASSERT(currentView(), return); @@ -669,7 +655,7 @@ void DiffEditor::setupView(IDiffView *view) saveSetting(QLatin1String(diffViewKeyC), currentView()->id().toSetting()); { - Guard guard(&m_ignoreChanges); + Utils::GuardLocker guard(m_ignoreChanges); m_toggleSyncAction->setVisible(currentView()->supportsSync()); m_toggleSyncAction->setToolTip(currentView()->syncToolTip()); m_toggleSyncAction->setText(currentView()->syncToolTip()); diff --git a/src/plugins/diffeditor/diffeditor.h b/src/plugins/diffeditor/diffeditor.h index 7539ff31d6a..bb8363187ee 100644 --- a/src/plugins/diffeditor/diffeditor.h +++ b/src/plugins/diffeditor/diffeditor.h @@ -29,6 +29,7 @@ #include #include +#include QT_BEGIN_NAMESPACE class QComboBox; @@ -103,7 +104,7 @@ private: QPair m_currentFileChunk; int m_currentViewIndex; int m_currentDiffFileIndex; - int m_ignoreChanges; + Utils::Guard m_ignoreChanges; bool m_sync; bool m_showDescription; }; diff --git a/src/plugins/diffeditor/diffeditordocument.cpp b/src/plugins/diffeditor/diffeditordocument.cpp index 5102df44d96..5a4d575d9c2 100644 --- a/src/plugins/diffeditor/diffeditordocument.cpp +++ b/src/plugins/diffeditor/diffeditordocument.cpp @@ -87,7 +87,9 @@ DiffEditorController *DiffEditorDocument::controller() const return m_controller; } -QString DiffEditorDocument::makePatch(int fileIndex, int chunkIndex, bool revert, bool addPrefix) const +QString DiffEditorDocument::makePatch(int fileIndex, int chunkIndex, + bool revert, bool addPrefix, + const QString &overriddenFileName) const { if (fileIndex < 0 || chunkIndex < 0) return QString(); @@ -102,9 +104,10 @@ QString DiffEditorDocument::makePatch(int fileIndex, int chunkIndex, bool revert const ChunkData &chunkData = fileData.chunks.at(chunkIndex); const bool lastChunk = (chunkIndex == fileData.chunks.count() - 1); - const QString fileName = revert - ? fileData.rightFileInfo.fileName - : fileData.leftFileInfo.fileName; + const QString fileName = !overriddenFileName.isEmpty() + ? overriddenFileName : revert + ? fileData.rightFileInfo.fileName + : fileData.leftFileInfo.fileName; QString leftPrefix, rightPrefix; if (addPrefix) { diff --git a/src/plugins/diffeditor/diffeditordocument.h b/src/plugins/diffeditor/diffeditordocument.h index 5b4771b0e71..18864711fe7 100644 --- a/src/plugins/diffeditor/diffeditordocument.h +++ b/src/plugins/diffeditor/diffeditordocument.h @@ -46,7 +46,9 @@ public: DiffEditorController *controller() const; - QString makePatch(int fileIndex, int chunkIndex, bool revert, bool addPrefix = false) const; + QString makePatch(int fileIndex, int chunkIndex, + bool revert, bool addPrefix = false, + const QString &overriddenFileName = QString()) const; void setDiffFiles(const QList &data, const QString &directory, const QString &startupFile = QString()); diff --git a/src/plugins/diffeditor/diffeditorplugin.cpp b/src/plugins/diffeditor/diffeditorplugin.cpp index db7936f34af..4eb0a07878a 100644 --- a/src/plugins/diffeditor/diffeditorplugin.cpp +++ b/src/plugins/diffeditor/diffeditorplugin.cpp @@ -44,10 +44,14 @@ #include #include +#include #include +#include #include +using namespace Core; + namespace DiffEditor { namespace Internal { @@ -55,20 +59,20 @@ class DiffFilesController : public DiffEditorController { Q_OBJECT public: - DiffFilesController(Core::IDocument *document); + DiffFilesController(IDocument *document); protected: FileData diffFiles(const QString &leftContents, const QString &rightContents); }; -DiffFilesController::DiffFilesController(Core::IDocument *document) +DiffFilesController::DiffFilesController(IDocument *document) : DiffEditorController(document) {} FileData DiffFilesController::diffFiles(const QString &leftContents, const QString &rightContents) { Differ differ; - QList diffList = differ.cleanupSemantics(differ.diff(leftContents, rightContents)); + const QList diffList = differ.cleanupSemantics(differ.diff(leftContents, rightContents)); QList leftDiffList; QList rightDiffList; @@ -90,7 +94,7 @@ FileData DiffFilesController::diffFiles(const QString &leftContents, const QStri const ChunkData chunkData = DiffUtils::calculateOriginalData( outputLeftDiffList, outputRightDiffList); - FileData fileData = DiffUtils::calculateContextData(chunkData, contextLineCount(), 0); + const FileData fileData = DiffUtils::calculateContextData(chunkData, contextLineCount(), 0); return fileData; } @@ -99,7 +103,7 @@ class DiffCurrentFileController : public DiffFilesController { Q_OBJECT public: - DiffCurrentFileController(Core::IDocument *document, const QString &fileName); + DiffCurrentFileController(IDocument *document, const QString &fileName); protected: void reload(); @@ -108,7 +112,7 @@ private: QString m_fileName; }; -DiffCurrentFileController::DiffCurrentFileController(Core::IDocument *document, const QString &fileName) : +DiffCurrentFileController::DiffCurrentFileController(IDocument *document, const QString &fileName) : DiffFilesController(document), m_fileName(fileName) { } @@ -117,7 +121,7 @@ void DiffCurrentFileController::reload() QList fileDataList; TextEditor::TextDocument *textDocument = qobject_cast( - Core::DocumentModel::documentForFilePath(m_fileName)); + DocumentModel::documentForFilePath(m_fileName)); if (textDocument && textDocument->isModified()) { QString errorString; @@ -139,6 +143,7 @@ void DiffCurrentFileController::reload() fileData.rightFileInfo.fileName = m_fileName; fileData.leftFileInfo.typeInfo = tr("Saved"); fileData.rightFileInfo.typeInfo = tr("Modified"); + fileData.rightFileInfo.patchBehaviour = DiffFileInfo::PatchEditor; if (!leftFileExists) fileData.fileOperation = FileData::NewFile; @@ -152,28 +157,28 @@ void DiffCurrentFileController::reload() ///////////////// -class DiffAllModifiedFilesController : public DiffFilesController +class DiffOpenFilesController : public DiffFilesController { Q_OBJECT public: - DiffAllModifiedFilesController(Core::IDocument *document); + DiffOpenFilesController(IDocument *document); protected: void reload(); }; -DiffAllModifiedFilesController::DiffAllModifiedFilesController(Core::IDocument *document) : +DiffOpenFilesController::DiffOpenFilesController(IDocument *document) : DiffFilesController(document) { } -void DiffAllModifiedFilesController::reload() +void DiffOpenFilesController::reload() { - QList openedDocuments = - Core::DocumentModel::openedDocuments(); + const QList openedDocuments = + DocumentModel::openedDocuments(); QList fileDataList; - foreach (Core::IDocument *doc, openedDocuments) { + foreach (IDocument *doc, openedDocuments) { TextEditor::TextDocument *textDocument = qobject_cast(doc); if (textDocument && textDocument->isModified()) { @@ -197,6 +202,68 @@ void DiffAllModifiedFilesController::reload() fileData.rightFileInfo.fileName = fileName; fileData.leftFileInfo.typeInfo = tr("Saved"); fileData.rightFileInfo.typeInfo = tr("Modified"); + fileData.rightFileInfo.patchBehaviour = DiffFileInfo::PatchEditor; + + if (!leftFileExists) + fileData.fileOperation = FileData::NewFile; + + fileDataList << fileData; + } + } + + setDiffFiles(fileDataList); + reloadFinished(true); +} + +///////////////// + +class DiffModifiedFilesController : public DiffFilesController +{ + Q_OBJECT +public: + DiffModifiedFilesController(IDocument *document, const QStringList &fileNames); + +protected: + void reload(); + +private: + QStringList m_fileNames; +}; + +DiffModifiedFilesController::DiffModifiedFilesController(IDocument *document, const QStringList &fileNames) : + DiffFilesController(document), m_fileNames(fileNames) +{ } + +void DiffModifiedFilesController::reload() +{ + QList fileDataList; + + foreach (const QString fileName, m_fileNames) { + TextEditor::TextDocument *textDocument = qobject_cast( + DocumentModel::documentForFilePath(fileName)); + + if (textDocument && textDocument->isModified()) { + QString errorString; + Utils::TextFileFormat format = textDocument->format(); + + QString leftText; + bool leftFileExists = true; + const QString fileName = textDocument->filePath().toString(); + if (Utils::TextFileFormat::readFile(fileName, + format.codec, + &leftText, &format, &errorString) + != Utils::TextFileFormat::ReadSuccess) { + leftFileExists = false; + } + + const QString rightText = textDocument->plainText(); + + FileData fileData = diffFiles(leftText, rightText); + fileData.leftFileInfo.fileName = fileName; + fileData.rightFileInfo.fileName = fileName; + fileData.leftFileInfo.typeInfo = tr("Saved"); + fileData.rightFileInfo.typeInfo = tr("Modified"); + fileData.rightFileInfo.patchBehaviour = DiffFileInfo::PatchEditor; if (!leftFileExists) fileData.fileOperation = FileData::NewFile; @@ -215,7 +282,7 @@ class DiffExternalFilesController : public DiffFilesController { Q_OBJECT public: - DiffExternalFilesController(Core::IDocument *document, const QString &leftFileName, + DiffExternalFilesController(IDocument *document, const QString &leftFileName, const QString &rightFileName); protected: @@ -226,7 +293,7 @@ private: QString m_rightFileName; }; -DiffExternalFilesController::DiffExternalFilesController(Core::IDocument *document, const QString &leftFileName, +DiffExternalFilesController::DiffExternalFilesController(IDocument *document, const QString &leftFileName, const QString &rightFileName) : DiffFilesController(document), m_leftFileName(leftFileName), m_rightFileName(rightFileName) { } @@ -235,7 +302,7 @@ void DiffExternalFilesController::reload() { QString errorString; Utils::TextFileFormat format; - format.codec = Core::EditorManager::defaultTextCodec(); + format.codec = EditorManager::defaultTextCodec(); QString leftText; bool leftFileExists = true; @@ -273,42 +340,79 @@ void DiffExternalFilesController::reload() ///////////////// + +static TextEditor::TextDocument *currentTextDocument() +{ + return qobject_cast( + EditorManager::currentDocument()); +} + +DiffEditorServiceImpl::DiffEditorServiceImpl(QObject *parent) : + QObject(parent) +{ +} + +void DiffEditorServiceImpl::diffModifiedFiles(const QStringList &fileNames) +{ + const QString documentId = QLatin1String("Diff Modified Files"); + const QString title = tr("Diff Modified Files"); + auto const document = qobject_cast( + DiffEditorController::findOrCreateDocument(documentId, title)); + if (!document) + return; + + if (!DiffEditorController::controller(document)) + new DiffModifiedFilesController(document, fileNames); + EditorManager::activateEditorForDocument(document); + document->reload(); +} + bool DiffEditorPlugin::initialize(const QStringList &arguments, QString *errorMessage) { Q_UNUSED(arguments) Q_UNUSED(errorMessage) //register actions - Core::ActionContainer *toolsContainer - = Core::ActionManager::actionContainer(Core::Constants::M_TOOLS); + ActionContainer *toolsContainer + = ActionManager::actionContainer(Core::Constants::M_TOOLS); toolsContainer->insertGroup(Core::Constants::G_TOOLS_OPTIONS, Constants::G_TOOLS_DIFF); - Core::ActionContainer *diffContainer = Core::ActionManager::createMenu("Diff"); + ActionContainer *diffContainer = ActionManager::createMenu("Diff"); diffContainer->menu()->setTitle(tr("&Diff")); toolsContainer->addMenu(diffContainer, Constants::G_TOOLS_DIFF); m_diffCurrentFileAction = new QAction(tr("Diff Current File"), this); - Core::Command *diffCurrentFileCommand = Core::ActionManager::registerAction(m_diffCurrentFileAction, "DiffEditor.DiffCurrentFile"); - diffCurrentFileCommand->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+H") : tr("Ctrl+H"))); + Command *diffCurrentFileCommand = ActionManager::registerAction(m_diffCurrentFileAction, "DiffEditor.DiffCurrentFile"); + diffCurrentFileCommand->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+H") : tr("Ctrl+H"))); connect(m_diffCurrentFileAction, &QAction::triggered, this, &DiffEditorPlugin::diffCurrentFile); diffContainer->addAction(diffCurrentFileCommand); - QAction *diffAllModifiedFilesAction = new QAction(tr("Diff All Modified Files"), this); - Core::Command *diffAllModifiedFilesCommand = Core::ActionManager::registerAction(diffAllModifiedFilesAction, "DiffEditor.DiffAllModifiedFiles"); - diffAllModifiedFilesCommand->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+Shift+H") : tr("Ctrl+Shift+H"))); - connect(diffAllModifiedFilesAction, &QAction::triggered, this, &DiffEditorPlugin::diffAllModifiedFiles); - diffContainer->addAction(diffAllModifiedFilesCommand); + m_diffOpenFilesAction = new QAction(tr("Diff Open Files"), this); + Command *diffOpenFilesCommand = ActionManager::registerAction(m_diffOpenFilesAction, "DiffEditor.DiffOpenFiles"); + diffOpenFilesCommand->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+Shift+H") : tr("Ctrl+Shift+H"))); + connect(m_diffOpenFilesAction, &QAction::triggered, this, &DiffEditorPlugin::diffOpenFiles); + diffContainer->addAction(diffOpenFilesCommand); QAction *diffExternalFilesAction = new QAction(tr("Diff External Files..."), this); - Core::Command *diffExternalFilesCommand = Core::ActionManager::registerAction(diffExternalFilesAction, "DiffEditor.DiffExternalFiles"); + Command *diffExternalFilesCommand = ActionManager::registerAction(diffExternalFilesAction, "DiffEditor.DiffExternalFiles"); connect(diffExternalFilesAction, &QAction::triggered, this, &DiffEditorPlugin::diffExternalFiles); diffContainer->addAction(diffExternalFilesCommand); - connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged, - this, &DiffEditorPlugin::updateCurrentEditor); + connect(EditorManager::instance(), &EditorManager::currentEditorChanged, + this, &DiffEditorPlugin::updateDiffCurrentFileAction); + connect(EditorManager::instance(), &EditorManager::currentDocumentStateChanged, + this, &DiffEditorPlugin::updateDiffCurrentFileAction); + connect(EditorManager::instance(), &EditorManager::editorOpened, + this, &DiffEditorPlugin::updateDiffOpenFilesAction); + connect(EditorManager::instance(), &EditorManager::editorsClosed, + this, &DiffEditorPlugin::updateDiffOpenFilesAction); + connect(EditorManager::instance(), &EditorManager::documentStateChanged, + this, &DiffEditorPlugin::updateDiffOpenFilesAction); - updateActions(); + updateDiffCurrentFileAction(); + updateDiffOpenFilesAction(); addAutoReleasedObject(new DiffEditorFactory(this)); + addAutoReleasedObject(new DiffEditorServiceImpl(this)); return true; } @@ -316,36 +420,28 @@ bool DiffEditorPlugin::initialize(const QStringList &arguments, QString *errorMe void DiffEditorPlugin::extensionsInitialized() { } -void DiffEditorPlugin::updateCurrentEditor(Core::IEditor *editor) +void DiffEditorPlugin::updateDiffCurrentFileAction() { - if (m_currentTextDocument) - m_currentTextDocument->disconnect(this); - m_currentTextDocument = 0; - - if (editor) { - TextEditor::TextEditorWidget *editorWidget = qobject_cast(editor->widget()); - if (editorWidget) { - m_currentTextDocument = editorWidget->textDocument(); - connect(m_currentTextDocument.data(), &Core::IDocument::changed, - this, &DiffEditorPlugin::updateActions); - } - } - - updateActions(); + auto textDocument = currentTextDocument(); + const bool enabled = textDocument && textDocument->isModified(); + m_diffCurrentFileAction->setEnabled(enabled); } -void DiffEditorPlugin::updateActions() +void DiffEditorPlugin::updateDiffOpenFilesAction() { - const bool diffCurrentFileEnabled = m_currentTextDocument && m_currentTextDocument->isModified(); - m_diffCurrentFileAction->setEnabled(diffCurrentFileEnabled); + const bool enabled = Utils::anyOf(DocumentModel::openedDocuments(), [](IDocument *doc) { + return doc->isModified() && qobject_cast(doc); + }); + m_diffOpenFilesAction->setEnabled(enabled); } void DiffEditorPlugin::diffCurrentFile() { - if (!m_currentTextDocument) + auto textDocument = currentTextDocument(); + if (!textDocument) return; - const QString fileName = m_currentTextDocument->filePath().toString(); + const QString fileName = textDocument->filePath().toString(); if (fileName.isEmpty()) return; @@ -359,34 +455,34 @@ void DiffEditorPlugin::diffCurrentFile() if (!DiffEditorController::controller(document)) new DiffCurrentFileController(document, fileName); - Core::EditorManager::activateEditorForDocument(document); + EditorManager::activateEditorForDocument(document); document->reload(); } -void DiffEditorPlugin::diffAllModifiedFiles() +void DiffEditorPlugin::diffOpenFiles() { - const QString documentId = QLatin1String("Diff All Modified Files"); - const QString title = tr("Diff All Modified Files"); + const QString documentId = QLatin1String("Diff Open Files"); + const QString title = tr("Diff Open Files"); auto const document = qobject_cast( DiffEditorController::findOrCreateDocument(documentId, title)); if (!document) return; if (!DiffEditorController::controller(document)) - new DiffAllModifiedFilesController(document); - Core::EditorManager::activateEditorForDocument(document); + new DiffOpenFilesController(document); + EditorManager::activateEditorForDocument(document); document->reload(); } void DiffEditorPlugin::diffExternalFiles() { - const QString fileName1 = QFileDialog::getOpenFileName(Core::ICore::dialogParent(), + const QString fileName1 = QFileDialog::getOpenFileName(ICore::dialogParent(), tr("Select First File for Diff"), QString()); if (fileName1.isNull()) return; - const QString fileName2 = QFileDialog::getOpenFileName(Core::ICore::dialogParent(), + const QString fileName2 = QFileDialog::getOpenFileName(ICore::dialogParent(), tr("Select Second File for Diff"), QString()); if (fileName2.isNull()) @@ -402,7 +498,7 @@ void DiffEditorPlugin::diffExternalFiles() if (!DiffEditorController::controller(document)) new DiffExternalFilesController(document, fileName1, fileName2); - Core::EditorManager::activateEditorForDocument(document); + EditorManager::activateEditorForDocument(document); document->reload(); } diff --git a/src/plugins/diffeditor/diffeditorplugin.h b/src/plugins/diffeditor/diffeditorplugin.h index ba0cab24337..a04f4609e3e 100644 --- a/src/plugins/diffeditor/diffeditorplugin.h +++ b/src/plugins/diffeditor/diffeditorplugin.h @@ -27,7 +27,7 @@ #include "diffeditor_global.h" -#include +#include #include QT_FORWARD_DECLARE_CLASS(QAction) @@ -37,6 +37,16 @@ namespace Core { class IEditor; } namespace DiffEditor { namespace Internal { +class DiffEditorServiceImpl : public QObject, public Core::DiffService +{ + Q_OBJECT + Q_INTERFACES(Core::DiffService) +public: + explicit DiffEditorServiceImpl(QObject *parent = nullptr); + + void diffModifiedFiles(const QStringList &fileNames) override; +}; + class DiffEditorPlugin : public ExtensionSystem::IPlugin { Q_OBJECT @@ -47,10 +57,10 @@ public: void extensionsInitialized(); private slots: - void updateCurrentEditor(Core::IEditor *editor); - void updateActions(); + void updateDiffCurrentFileAction(); + void updateDiffOpenFilesAction(); void diffCurrentFile(); - void diffAllModifiedFiles(); + void diffOpenFiles(); void diffExternalFiles(); #ifdef WITH_TESTS @@ -61,7 +71,7 @@ private slots: #endif // WITH_TESTS private: QAction *m_diffCurrentFileAction; - QPointer m_currentTextDocument; + QAction *m_diffOpenFilesAction; }; } // namespace Internal diff --git a/src/plugins/diffeditor/diffeditorwidgetcontroller.cpp b/src/plugins/diffeditor/diffeditorwidgetcontroller.cpp index 8c4e182b229..3e30a0d8204 100644 --- a/src/plugins/diffeditor/diffeditorwidgetcontroller.cpp +++ b/src/plugins/diffeditor/diffeditorwidgetcontroller.cpp @@ -32,6 +32,7 @@ #include #include +#include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include using namespace Core; @@ -77,27 +79,60 @@ void DiffEditorWidgetController::patch(bool revert) return; } - const int strip = m_document->baseDirectory().isEmpty() ? -1 : 0; - const FileData fileData = m_contextFileData.at(m_contextMenuFileIndex); const QString fileName = revert ? fileData.rightFileInfo.fileName : fileData.leftFileInfo.fileName; + const DiffFileInfo::PatchBehaviour patchBehaviour = revert + ? fileData.rightFileInfo.patchBehaviour + : fileData.leftFileInfo.patchBehaviour; const QString workingDirectory = m_document->baseDirectory().isEmpty() ? QFileInfo(fileName).absolutePath() : m_document->baseDirectory(); + const QString absFileName = QFileInfo(workingDirectory + '/' + QFileInfo(fileName).fileName()).absoluteFilePath(); - const QString patch = m_document->makePatch(m_contextMenuFileIndex, m_contextMenuChunkIndex, revert); + if (patchBehaviour == DiffFileInfo::PatchFile) { + const int strip = m_document->baseDirectory().isEmpty() ? -1 : 0; - if (patch.isEmpty()) - return; + const QString patch = m_document->makePatch(m_contextMenuFileIndex, m_contextMenuChunkIndex, revert); - const QString absFileName = QFileInfo(workingDirectory + '/' + fileName).absoluteFilePath(); - FileChangeBlocker fileChangeBlocker(absFileName); - if (PatchTool::runPatch(EditorManager::defaultTextCodec()->fromUnicode(patch), - workingDirectory, strip, revert)) - m_document->reload(); + if (patch.isEmpty()) + return; + + FileChangeBlocker fileChangeBlocker(absFileName); + if (PatchTool::runPatch(EditorManager::defaultTextCodec()->fromUnicode(patch), + workingDirectory, strip, revert)) + m_document->reload(); + } else { // PatchEditor + TextEditor::TextDocument *textDocument = qobject_cast( + DocumentModel::documentForFilePath(absFileName)); + if (!textDocument) + return; + + QTemporaryFile contentsCopy; + if (!contentsCopy.open()) + return; + + contentsCopy.write(textDocument->contents()); + contentsCopy.close(); + + const QString contentsCopyFileName = contentsCopy.fileName(); + const QString contentsCopyDir = QFileInfo(contentsCopyFileName).absolutePath(); + + const QString patch = m_document->makePatch(m_contextMenuFileIndex, + m_contextMenuChunkIndex, revert, false, QFileInfo(contentsCopyFileName).fileName()); + + if (patch.isEmpty()) + return; + + if (PatchTool::runPatch(EditorManager::defaultTextCodec()->fromUnicode(patch), + contentsCopyDir, 0, revert)) { + QString errorString; + if (textDocument->reload(&errorString, contentsCopyFileName)) + m_document->reload(); + } + } } void DiffEditorWidgetController::jumpToOriginalFile(const QString &fileName, diff --git a/src/plugins/diffeditor/diffutils.h b/src/plugins/diffeditor/diffutils.h index 417309294b9..d588a52ad8f 100644 --- a/src/plugins/diffeditor/diffutils.h +++ b/src/plugins/diffeditor/diffutils.h @@ -38,12 +38,18 @@ class Diff; class DIFFEDITOR_EXPORT DiffFileInfo { public: + enum PatchBehaviour { + PatchFile, + PatchEditor + }; + DiffFileInfo() {} DiffFileInfo(const QString &file) : fileName(file) {} DiffFileInfo(const QString &file, const QString &type) : fileName(file), typeInfo(type) {} QString fileName; QString typeInfo; + PatchBehaviour patchBehaviour = PatchFile; }; class DIFFEDITOR_EXPORT TextLineData { diff --git a/src/plugins/modeleditor/extpropertiesmview.cpp b/src/plugins/modeleditor/extpropertiesmview.cpp index 20376216583..529f251dca2 100644 --- a/src/plugins/modeleditor/extpropertiesmview.cpp +++ b/src/plugins/modeleditor/extpropertiesmview.cpp @@ -92,7 +92,7 @@ void ExtPropertiesMView::onConfigPathChanged(const QString &path) if (path.isEmpty()) { if (!project->configPath().isEmpty()) { project->setConfigPath(QString()); - m_projectController->setModified(true); + m_projectController->setModified(); modified = true; } } else { @@ -103,7 +103,7 @@ void ExtPropertiesMView::onConfigPathChanged(const QString &path) QString configPath = projectDir.relativeFilePath(absConfigPath.filePath()); if (configPath != project->configPath()) { project->setConfigPath(configPath); - m_projectController->setModified(true); + m_projectController->setModified(); modified = true; } } diff --git a/src/plugins/modeleditor/modeldocument.cpp b/src/plugins/modeleditor/modeldocument.cpp index 82a38f19ef8..e75ff0760e1 100644 --- a/src/plugins/modeleditor/modeldocument.cpp +++ b/src/plugins/modeleditor/modeldocument.cpp @@ -94,7 +94,7 @@ bool ModelDocument::save(QString *errorString, const QString &name, bool autoSav } if (autoSave) { - d->documentController->projectController()->setModified(true); + d->documentController->projectController()->setModified(); } else { setFilePath(Utils::FileName::fromString(d->documentController->projectController()->project()->fileName())); emit changed(); @@ -108,6 +108,11 @@ bool ModelDocument::shouldAutoSave() const return isModified(); } +bool ModelDocument::isModified() const +{ + return d->documentController ? d->documentController->projectController()->isModified() : false; +} + bool ModelDocument::isSaveAsAllowed() const { return true; @@ -135,10 +140,6 @@ Core::IDocument::OpenResult ModelDocument::load(QString *errorString, const QStr { d->documentController = ModelEditorPlugin::modelsManager()->createModel(this); connect(d->documentController, &qmt::DocumentController::changed, this, &IDocument::changed); - connect(d->documentController, &qmt::DocumentController::modificationChanged, this, &IDocument::setModified); - connect(this, &IDocument::modificationChanged, - d->documentController->projectController(), &qmt::ProjectController::setModified); - setModified(d->documentController->projectController()->isModified()); try { d->documentController->loadProject(fileName); diff --git a/src/plugins/modeleditor/modeldocument.h b/src/plugins/modeleditor/modeldocument.h index 2d0a0aafee2..f83085866ab 100644 --- a/src/plugins/modeleditor/modeldocument.h +++ b/src/plugins/modeleditor/modeldocument.h @@ -52,6 +52,7 @@ public: const QString &realFileName) override; bool save(QString *errorString, const QString &fileName, bool autoSave) override; bool shouldAutoSave() const override; + bool isModified() const override; bool isSaveAsAllowed() const override; bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override; diff --git a/src/plugins/projectexplorer/targetsettingspanel.cpp b/src/plugins/projectexplorer/targetsettingspanel.cpp index d3f557f219c..49ebd5e8564 100644 --- a/src/plugins/projectexplorer/targetsettingspanel.cpp +++ b/src/plugins/projectexplorer/targetsettingspanel.cpp @@ -34,6 +34,7 @@ #include "panelswidget.h" #include "project.h" #include "projectexplorer.h" +#include "projectexplorericons.h" #include "projectwindow.h" #include "runsettingspropertiespage.h" #include "session.h" @@ -563,8 +564,19 @@ public: case KitIdRole: return m_kitId.toSetting(); - case Qt::DecorationRole: - return Utils::Icons::EMPTY14.icon(); + case Qt::DecorationRole: { + switch (m_subIndex) { + case BuildPage: { + static const QIcon buildIcon = ProjectExplorer::Icons::BUILD_SMALL.icon(); + return buildIcon; + } + case RunPage: { + static const QIcon runIcon = Utils::Icons::RUN_SMALL.icon(); + return runIcon; + } + } + break; + } default: break; diff --git a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp index 9fbeb73176f..a83e814a300 100644 --- a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp +++ b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp @@ -263,8 +263,10 @@ QVariantMap DefaultPropertyProvider::autoGeneratedProperties(const ProjectExplor if (toolchain.contains(QLatin1String("msvc"))) { data.insert(QLatin1String(CPP_COMPILERNAME), mainCompilerName); } else { - data.insert(QLatin1String(CPP_COMPILERNAME), cCompilerName); - data.insert(QLatin1String(CPP_CXXCOMPILERNAME), cxxCompilerName); + if (!cCompilerName.isEmpty()) + data.insert(QLatin1String(CPP_COMPILERNAME), cCompilerName); + if (!cxxCompilerName.isEmpty()) + data.insert(QLatin1String(CPP_CXXCOMPILERNAME), cxxCompilerName); } if (tcC && tcCxx && cFileInfo.absolutePath() != cxxFileInfo.absolutePath()) { diff --git a/src/plugins/qbsprojectmanager/qbsnodes.cpp b/src/plugins/qbsprojectmanager/qbsnodes.cpp index 58dd0d23af2..28612849c0f 100644 --- a/src/plugins/qbsprojectmanager/qbsnodes.cpp +++ b/src/plugins/qbsprojectmanager/qbsnodes.cpp @@ -710,10 +710,12 @@ void QbsProductNode::setQbsProductData(const qbs::Project &project, const qbs::P } } - const QStringList generatedFiles - = Utils::transform(prd.generatedArtifacts(), &qbs::ArtifactData::filePath); - QbsGroupNode::setupFiles(m_generatedFilesNode, qbs::GroupData(), generatedFiles, - prd.buildDirectory(), true, true); + if (prd.isEnabled()) { + const QStringList generatedFiles + = Utils::transform(prd.generatedArtifacts(), &qbs::ArtifactData::filePath); + QbsGroupNode::setupFiles(m_generatedFilesNode, qbs::GroupData(), generatedFiles, + prd.buildDirectory(), true, true); + } addProjectNodes(toAdd); removeProjectNodes(toRemove); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index f5e476aeaed..182b717f23b 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -670,8 +670,9 @@ void addSignalHandlerOrGotoImplementation(const SelectionContext &selectionState QString itemId = modelNode.id(); - const QString fileName = QmlDesignerPlugin::instance()->documentManager().currentDesignDocument()->fileName().toString(); - const QString typeName = QmlDesignerPlugin::instance()->documentManager().currentDesignDocument()->fileName().toFileInfo().baseName(); + const Utils::FileName currentDesignDocument = QmlDesignerPlugin::instance()->documentManager().currentDesignDocument()->fileName(); + const QString fileName = currentDesignDocument.toString(); + const QString typeName = currentDesignDocument.toFileInfo().baseName(); QStringList signalNames = cleanSignalNames(getSortedSignalNameList(selectionState.selectedModelNodes().first())); diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 7004107a645..28630440521 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -134,7 +134,7 @@ static ComponentTextModifier *createComponentTextModifier(TextModifier *original componentEndOffset = componentStartOffset + rewriterView->nodeLength(componentNode); } - return new ComponentTextModifier (originalModifier, componentStartOffset, componentEndOffset, rootStartOffset); + return new ComponentTextModifier(originalModifier, componentStartOffset, componentEndOffset, rootStartOffset); } bool DesignDocument::loadInFileComponent(const ModelNode &componentNode) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index 60ce7234f28..4f354746d65 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -65,6 +65,7 @@ WidgetInfo ItemLibraryView::widgetInfo() void ItemLibraryView::modelAttached(Model *model) { AbstractView::modelAttached(model); + m_widget->clearSearchFilter(); m_widget->setModel(model); updateImports(); model->attachView(m_importManagerView); @@ -75,6 +76,7 @@ void ItemLibraryView::modelAboutToBeDetached(Model *model) model->detachView(m_importManagerView); AbstractView::modelAboutToBeDetached(model); + m_widget->setModel(0); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 5820c518ac2..039a2be448f 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -247,6 +247,11 @@ QString ItemLibraryWidget::qmlSourcesPath() return Core::ICore::resourcePath() + QStringLiteral("/qmldesigner/itemLibraryQmlSources"); } +void ItemLibraryWidget::clearSearchFilter() +{ + m_filterLineEdit->clear(); +} + void ItemLibraryWidget::reloadQmlSource() { QString itemLibraryQmlFilePath = qmlSourcesPath() + QStringLiteral("/ItemsView.qml"); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index 6a39035b53e..ca8cd82c319 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -86,6 +86,8 @@ public: void setImportsWidget(QWidget *importsWidget); static QString qmlSourcesPath(); + void clearSearchFilter(); + public slots: void setSearchFilter(const QString &searchFilter); void delayedUpdateModel(); diff --git a/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h b/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h index e5b1bd368cd..e20566724bf 100644 --- a/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h +++ b/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h @@ -41,6 +41,7 @@ class QMLDESIGNERCORE_EXPORT BaseTextEditModifier: public PlainTextEditModifier public: BaseTextEditModifier(TextEditor::TextEditorWidget *textEdit); + void indentLines(int startLine, int endLine) override; void indent(int offset, int length) override; int indentDepth() const override; diff --git a/src/plugins/qmldesigner/designercore/include/componenttextmodifier.h b/src/plugins/qmldesigner/designercore/include/componenttextmodifier.h index 194ebd2d5bb..01b8f63e539 100644 --- a/src/plugins/qmldesigner/designercore/include/componenttextmodifier.h +++ b/src/plugins/qmldesigner/designercore/include/componenttextmodifier.h @@ -39,6 +39,7 @@ public: void replace(int offset, int length, const QString& replacement) override; void move(const MoveInfo &moveInfo) override; void indent(int offset, int length) override; + void indentLines(int startLine, int endLine) override; int indentDepth() const override; diff --git a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h index cd866c94691..210f2c30408 100644 --- a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h +++ b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h @@ -58,6 +58,7 @@ public: void replace(int offset, int length, const QString &replacement) override; void move(const MoveInfo &moveInfo) override; void indent(int offset, int length) override = 0; + void indentLines(int startLine, int endLine) override = 0; int indentDepth() const override = 0; @@ -102,10 +103,12 @@ public: : PlainTextEditModifier(textEdit) {} - virtual void indent(int /*offset*/, int /*length*/) + void indent(int /*offset*/, int /*length*/) override + {} + void indentLines(int /*offset*/, int /*length*/) override {} - virtual int indentDepth() const + int indentDepth() const override { return 0; } }; diff --git a/src/plugins/qmldesigner/designercore/include/textmodifier.h b/src/plugins/qmldesigner/designercore/include/textmodifier.h index 98a9d671a77..d0c4cebeb4d 100644 --- a/src/plugins/qmldesigner/designercore/include/textmodifier.h +++ b/src/plugins/qmldesigner/designercore/include/textmodifier.h @@ -64,6 +64,7 @@ public: virtual void replace(int offset, int length, const QString& replacement) = 0; virtual void move(const MoveInfo &moveInfo) = 0; virtual void indent(int offset, int length) = 0; + virtual void indentLines(int startLine, int endLine) = 0; virtual int indentDepth() const = 0; @@ -74,6 +75,7 @@ public: virtual QTextDocument *textDocument() const = 0; virtual QString text() const = 0; virtual QTextCursor textCursor() const = 0; + static int getLineInDocument(QTextDocument* document, int offset); virtual void deactivateChangeSignals() = 0; virtual void reactivateChangeSignals() = 0; diff --git a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp index 1324e5a7b25..ab68cc16054 100644 --- a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp +++ b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp @@ -43,41 +43,41 @@ BaseTextEditModifier::BaseTextEditModifier(TextEditor::TextEditorWidget *textEdi { } +void BaseTextEditModifier::indentLines(int startLine, int endLine) +{ + if (startLine < 0) + return; + TextEditor::TextEditorWidget *baseTextEditorWidget = qobject_cast(plainTextEdit()); + if (!baseTextEditorWidget) + return; + + QTextDocument *textDocument = plainTextEdit()->document(); + TextEditor::TextDocument *baseTextEditorDocument = baseTextEditorWidget->textDocument(); + TextEditor::TabSettings tabSettings = baseTextEditorDocument->tabSettings(); + QTextCursor tc(textDocument); + + tc.beginEditBlock(); + for (int i = startLine; i <= endLine; i++) { + QTextBlock start = textDocument->findBlockByNumber(i); + + if (start.isValid()) { + QmlJSEditor::Internal::Indenter indenter; + indenter.indentBlock(textDocument, start, QChar::Null, tabSettings); + } + } + tc.endEditBlock(); +} + void BaseTextEditModifier::indent(int offset, int length) { if (length == 0 || offset < 0 || offset + length >= text().length()) return; - if (TextEditor::TextEditorWidget *baseTextEditorWidget = qobject_cast(plainTextEdit())) { + int startLine = getLineInDocument(textDocument(), offset); + int endLine = getLineInDocument(textDocument(), offset + length); - TextEditor::TextDocument *baseTextEditorDocument = baseTextEditorWidget->textDocument(); - QTextDocument *textDocument = baseTextEditorWidget->document(); - - int startLine = -1; - int endLine = -1; - int column; - - baseTextEditorWidget->convertPosition(offset, &startLine, &column); //get line - baseTextEditorWidget->convertPosition(offset + length, &endLine, &column); //get line - - QTextDocument *doc = baseTextEditorDocument->document(); - QTextCursor tc(doc); - tc.beginEditBlock(); - - if (startLine > 0) { - TextEditor::TabSettings tabSettings = baseTextEditorDocument->tabSettings(); - for (int i = startLine; i <= endLine; i++) { - QTextBlock start = textDocument->findBlockByNumber(i); - - if (start.isValid()) { - QmlJSEditor::Internal::Indenter indenter; - indenter.indentBlock(textDocument, start, QChar::Null, tabSettings); - } - } - } - - tc.endEditBlock(); - } + if (startLine > -1 && endLine > -1) + indentLines(startLine, endLine); } int BaseTextEditModifier::indentDepth() const @@ -134,7 +134,6 @@ bool BaseTextEditModifier::moveToComponent(int nodeOffset) QmlJSEditor::ComponentFromObjectDef::perform(document->filePath().toString(), object); return true; - } } return false; diff --git a/src/plugins/qmldesigner/designercore/model/componenttextmodifier.cpp b/src/plugins/qmldesigner/designercore/model/componenttextmodifier.cpp index 45d6ddc2b00..a91e47e61e3 100644 --- a/src/plugins/qmldesigner/designercore/model/componenttextmodifier.cpp +++ b/src/plugins/qmldesigner/designercore/model/componenttextmodifier.cpp @@ -26,6 +26,7 @@ #include "componenttextmodifier.h" using namespace QmlDesigner; + ComponentTextModifier::ComponentTextModifier(TextModifier *originalModifier, int componentStartOffset, int componentEndOffset, int rootStartOffset) : m_originalModifier(originalModifier), m_componentStartOffset(componentStartOffset), @@ -57,6 +58,11 @@ void ComponentTextModifier::indent(int offset, int length) m_originalModifier->indent(offset, length); } +void ComponentTextModifier::indentLines(int startLine, int endLine) +{ + m_originalModifier->indentLines(startLine, endLine); +} + int ComponentTextModifier::indentDepth() const { return m_originalModifier->indentDepth(); diff --git a/src/plugins/qmldesigner/designercore/model/textmodifier.cpp b/src/plugins/qmldesigner/designercore/model/textmodifier.cpp index 3434b21497f..8b5ea35ff1c 100644 --- a/src/plugins/qmldesigner/designercore/model/textmodifier.cpp +++ b/src/plugins/qmldesigner/designercore/model/textmodifier.cpp @@ -26,6 +26,7 @@ #include "textmodifier.h" #include +#include using namespace QmlDesigner; @@ -33,6 +34,14 @@ TextModifier::~TextModifier() { } +int TextModifier::getLineInDocument(QTextDocument *document, int offset) +{ + int line = -1; + int column = -1; + TextEditor::Convenience::convertPosition(document, offset, &line, &column); + return line; +} + QmlJS::Snapshot TextModifier::qmljsSnapshot() { QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance(); diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index 01ec2dfec66..d1aecf0ee19 100644 --- a/src/plugins/qmldesigner/documentmanager.cpp +++ b/src/plugins/qmldesigner/documentmanager.cpp @@ -53,13 +53,14 @@ namespace QmlDesigner { Q_LOGGING_CATEGORY(documentManagerLog, "qtc.qtquickdesigner.documentmanager") -static inline DesignDocument* currentDesignDocument() +static inline QmlDesigner::DesignDocument* designDocument() { - return QmlDesignerPlugin::instance()->documentManager().currentDesignDocument(); + return QmlDesigner::QmlDesignerPlugin::instance()->documentManager().currentDesignDocument(); } -static inline void getProperties(const ModelNode &node, QHash &propertyHash) +static inline QHash getProperties(const ModelNode &node) { + QHash propertyHash; if (QmlObjectNode::isValidQmlObjectNode(node)) { foreach (const AbstractProperty &abstractProperty, node.properties()) { if (abstractProperty.isVariantProperty() @@ -79,6 +80,7 @@ static inline void getProperties(const ModelNode &node, QHash &propertyHash) @@ -111,14 +113,8 @@ static inline void applyProperties(ModelNode &node, const QHashviewManager().nextFileIsCalledInternally(); - - QHash propertyHash; - - getProperties(modelNode, propertyHash); - Core::EditorManager::openEditor(modelNode.metaInfo().componentFileName(), Core::Id(), Core::EditorManager::DoNotMakeVisible); - - ModelNode rootModelNode = currentDesignDocument()->rewriterView()->rootModelNode(); - applyProperties(rootModelNode, propertyHash); + Core::EditorManager::openEditor(modelNode.metaInfo().componentFileName(), + Core::Id(), Core::EditorManager::DoNotMakeVisible); } static void openFileComponentForDelegate(const ModelNode &modelNode) @@ -130,10 +126,6 @@ static void openComponentSourcePropertyOfLoader(const ModelNode &modelNode) { QmlDesignerPlugin::instance()->viewManager().nextFileIsCalledInternally(); - QHash propertyHash; - - getProperties(modelNode, propertyHash); - ModelNode componentModelNode; if (modelNode.hasNodeProperty("sourceComponent")) { @@ -149,32 +141,23 @@ static void openComponentSourcePropertyOfLoader(const ModelNode &modelNode) } Core::EditorManager::openEditor(componentModelNode.metaInfo().componentFileName(), Core::Id(), Core::EditorManager::DoNotMakeVisible); - - ModelNode rootModelNode = currentDesignDocument()->rewriterView()->rootModelNode(); - applyProperties(rootModelNode, propertyHash); } static void openSourcePropertyOfLoader(const ModelNode &modelNode) { QmlDesignerPlugin::instance()->viewManager().nextFileIsCalledInternally(); - QHash propertyHash; - QString componentFileName = modelNode.variantProperty("source").value().toString(); QString componentFilePath = modelNode.model()->fileUrl().resolved(QUrl::fromLocalFile(componentFileName)).toLocalFile(); - getProperties(modelNode, propertyHash); Core::EditorManager::openEditor(componentFilePath, Core::Id(), Core::EditorManager::DoNotMakeVisible); - - ModelNode rootModelNode = currentDesignDocument()->rewriterView()->rootModelNode(); - applyProperties(rootModelNode, propertyHash); } static void handleComponent(const ModelNode &modelNode) { if (modelNode.nodeSourceType() == ModelNode::NodeWithComponentSource) - currentDesignDocument()->changeToSubComponent(modelNode); + designDocument()->changeToSubComponent(modelNode); } static void handleDelegate(const ModelNode &modelNode) @@ -182,36 +165,25 @@ static void handleDelegate(const ModelNode &modelNode) if (modelNode.metaInfo().isView() && modelNode.hasNodeProperty("delegate") && modelNode.nodeProperty("delegate").modelNode().nodeSourceType() == ModelNode::NodeWithComponentSource) - currentDesignDocument()->changeToSubComponent(modelNode.nodeProperty("delegate").modelNode()); + designDocument()->changeToSubComponent(modelNode.nodeProperty("delegate").modelNode()); } static void handleTabComponent(const ModelNode &modelNode) { if (modelNode.hasNodeProperty("component") && modelNode.nodeProperty("component").modelNode().nodeSourceType() == ModelNode::NodeWithComponentSource) { - currentDesignDocument()->changeToSubComponent(modelNode.nodeProperty("component").modelNode()); + designDocument()->changeToSubComponent(modelNode.nodeProperty("component").modelNode()); } } static inline void openInlineComponent(const ModelNode &modelNode) { - - if (!modelNode.isValid() || !modelNode.metaInfo().isValid()) + if (!modelNode.metaInfo().isValid()) return; - if (!currentDesignDocument()) - return; - - QHash propertyHash; - - getProperties(modelNode, propertyHash); - handleComponent(modelNode); handleDelegate(modelNode); handleTabComponent(modelNode); - - ModelNode rootModelNode = currentDesignDocument()->rewriterView()->rootModelNode(); - applyProperties(rootModelNode, propertyHash); } static bool isFileComponent(const ModelNode &node) @@ -249,7 +221,6 @@ static bool isLoaderWithSourceComponent(const ModelNode &modelNode) } return false; - } static bool hasSourceWithFileComponent(const ModelNode &modelNode) @@ -295,7 +266,7 @@ DesignDocument *DocumentManager::currentDesignDocument() const bool DocumentManager::hasCurrentDesignDocument() const { - return m_currentDesignDocument.data(); + return !m_currentDesignDocument.isNull(); } void DocumentManager::removeEditors(const QList &editors) @@ -306,8 +277,9 @@ void DocumentManager::removeEditors(const QList &editors) void DocumentManager::goIntoComponent(const ModelNode &modelNode) { - if (modelNode.isValid() && modelNode.isComponent()) { + if (modelNode.isValid() && modelNode.isComponent() && designDocument()) { QmlDesignerPlugin::instance()->viewManager().setComponentNode(modelNode); + QHash oldProperties = getProperties(modelNode); if (isFileComponent(modelNode)) openFileComponent(modelNode); else if (hasDelegateWithFileComponent(modelNode)) @@ -318,6 +290,8 @@ void DocumentManager::goIntoComponent(const ModelNode &modelNode) openComponentSourcePropertyOfLoader(modelNode); else openInlineComponent(modelNode); + ModelNode rootModelNode = designDocument()->rewriterView()->rootModelNode(); + applyProperties(rootModelNode, oldProperties); } } diff --git a/src/plugins/resourceeditor/resourceeditorw.cpp b/src/plugins/resourceeditor/resourceeditorw.cpp index 765aa5e1104..2058c3a2eaf 100644 --- a/src/plugins/resourceeditor/resourceeditorw.cpp +++ b/src/plugins/resourceeditor/resourceeditorw.cpp @@ -63,14 +63,10 @@ ResourceEditorDocument::ResourceEditorDocument(QObject *parent) : setId(ResourceEditor::Constants::RESOURCEEDITOR_ID); setMimeType(QLatin1String(ResourceEditor::Constants::C_RESOURCE_MIMETYPE)); connect(m_model, &RelativeResourceModel::dirtyChanged, - this, &ResourceEditorDocument::setModified); - connect(this, &IDocument::modificationChanged, - m_model, &RelativeResourceModel::setDirty); + this, &ResourceEditorDocument::dirtyChanged); connect(m_model, &ResourceModel::contentsChanged, this, &IDocument::contentsChanged); - setModified(m_model->dirty()); - if (debugResourceEditorW) qDebug() << "ResourceEditorFile::ResourceEditorFile()"; } @@ -128,16 +124,20 @@ Core::IDocument::OpenResult ResourceEditorDocument::open(QString *errorString, if (debugResourceEditorW) qDebug() << "ResourceEditorW::open: " << fileName; + setBlockDirtyChanged(true); + m_model->setFileName(realFileName); OpenResult openResult = m_model->reload(); if (openResult != OpenResult::Success) { *errorString = m_model->errorMessage(); + setBlockDirtyChanged(false); emit loaded(false); return openResult; } setFilePath(FileName::fromString(fileName)); + setBlockDirtyChanged(false); m_model->setDirty(fileName != realFileName); m_shouldAutoSave = false; @@ -155,10 +155,12 @@ bool ResourceEditorDocument::save(QString *errorString, const QString &name, boo if (actualName.isEmpty()) return false; + m_blockDirtyChanged = true; m_model->setFileName(actualName.toString()); if (!m_model->save()) { *errorString = m_model->errorMessage(); m_model->setFileName(oldFileName.toString()); + m_blockDirtyChanged = false; return false; } @@ -166,10 +168,12 @@ bool ResourceEditorDocument::save(QString *errorString, const QString &name, boo if (autoSave) { m_model->setFileName(oldFileName.toString()); m_model->setDirty(true); + m_blockDirtyChanged = false; return true; } setFilePath(actualName); + m_blockDirtyChanged = false; emit changed(); return true; @@ -209,6 +213,11 @@ void ResourceEditorDocument::setFilePath(const FileName &newName) IDocument::setFilePath(newName); } +void ResourceEditorDocument::setBlockDirtyChanged(bool value) +{ + m_blockDirtyChanged = value; +} + RelativeResourceModel *ResourceEditorDocument::model() const { return m_model; @@ -229,6 +238,11 @@ bool ResourceEditorDocument::shouldAutoSave() const return m_shouldAutoSave; } +bool ResourceEditorDocument::isModified() const +{ + return m_model->dirty(); +} + bool ResourceEditorDocument::isSaveAsAllowed() const { return true; @@ -250,6 +264,16 @@ bool ResourceEditorDocument::reload(QString *errorString, ReloadFlag flag, Chang return true; } +void ResourceEditorDocument::dirtyChanged(bool dirty) +{ + if (m_blockDirtyChanged) + return; // We emit changed() afterwards, unless it was an autosave + + if (debugResourceEditorW) + qDebug() << " ResourceEditorW::dirtyChanged" << dirty; + emit changed(); +} + void ResourceEditorW::onUndoStackChanged(bool canUndo, bool canRedo) { m_plugin->onUndoStackChanged(this, canUndo, canRedo); diff --git a/src/plugins/resourceeditor/resourceeditorw.h b/src/plugins/resourceeditor/resourceeditorw.h index 0ac7005df4f..3d8d139e3c2 100644 --- a/src/plugins/resourceeditor/resourceeditorw.h +++ b/src/plugins/resourceeditor/resourceeditorw.h @@ -58,9 +58,11 @@ public: QByteArray contents() const override; bool setContents(const QByteArray &contents) override; bool shouldAutoSave() const override; + bool isModified() const override; bool isSaveAsAllowed() const override; bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override; void setFilePath(const Utils::FileName &newName) override; + void setBlockDirtyChanged(bool value); RelativeResourceModel *model() const; void setShouldAutoSave(bool save); @@ -69,8 +71,10 @@ signals: void loaded(bool success); private: + void dirtyChanged(bool); RelativeResourceModel *m_model; + bool m_blockDirtyChanged = false; bool m_shouldAutoSave = false; }; diff --git a/src/plugins/scxmleditor/common/common.qrc b/src/plugins/scxmleditor/common/common.qrc index 3106d4ae190..49ee31ce666 100644 --- a/src/plugins/scxmleditor/common/common.qrc +++ b/src/plugins/scxmleditor/common/common.qrc @@ -15,6 +15,7 @@ images/fullnamespace.png images/history.png images/icon-export-canvas.png + images/icon-export-canvas@2x.png images/icon-fit-screen.png images/icon-pan.png images/icon-zoom-in.png @@ -24,7 +25,6 @@ images/navigator.png images/parallel.png images/parallel_icon.png - images/screenshot.png images/state.png images/state_color.png images/statistics.png diff --git a/src/plugins/scxmleditor/common/images/icon-export-canvas.png b/src/plugins/scxmleditor/common/images/icon-export-canvas.png index 4c0eca55602..d2d47205a8f 100644 Binary files a/src/plugins/scxmleditor/common/images/icon-export-canvas.png and b/src/plugins/scxmleditor/common/images/icon-export-canvas.png differ diff --git a/src/plugins/scxmleditor/common/images/icon-export-canvas@2x.png b/src/plugins/scxmleditor/common/images/icon-export-canvas@2x.png new file mode 100644 index 00000000000..bdc82b7ade6 Binary files /dev/null and b/src/plugins/scxmleditor/common/images/icon-export-canvas@2x.png differ diff --git a/src/plugins/scxmleditor/common/images/screenshot.png b/src/plugins/scxmleditor/common/images/screenshot.png deleted file mode 100644 index 1f2378ab38d..00000000000 Binary files a/src/plugins/scxmleditor/common/images/screenshot.png and /dev/null differ diff --git a/src/plugins/scxmleditor/common/mainwidget.cpp b/src/plugins/scxmleditor/common/mainwidget.cpp index e68b5736008..09c00c0f623 100644 --- a/src/plugins/scxmleditor/common/mainwidget.cpp +++ b/src/plugins/scxmleditor/common/mainwidget.cpp @@ -789,11 +789,6 @@ bool MainWidget::isDirty() const return m_document->changed(); } -void MainWidget::setDirty(bool dirty) -{ - m_document->setChanged(dirty); -} - void MainWidget::fitToView() { StateView *view = m_views.last(); diff --git a/src/plugins/scxmleditor/common/mainwidget.h b/src/plugins/scxmleditor/common/mainwidget.h index 64e17a97303..6b75895b0f3 100644 --- a/src/plugins/scxmleditor/common/mainwidget.h +++ b/src/plugins/scxmleditor/common/mainwidget.h @@ -87,7 +87,6 @@ public: QString contents() const; QUndoStack *undoStack() const; bool isDirty() const; - void setDirty(bool dirty); void newDocument(); void refresh(); OutputPane::WarningModel *warningModel() const; diff --git a/src/plugins/scxmleditor/plugin_interface/actionhandler.cpp b/src/plugins/scxmleditor/plugin_interface/actionhandler.cpp index d2cfe7f5ff6..076be01876b 100644 --- a/src/plugins/scxmleditor/plugin_interface/actionhandler.cpp +++ b/src/plugins/scxmleditor/plugin_interface/actionhandler.cpp @@ -55,8 +55,8 @@ ActionHandler::ActionHandler(QObject *parent) { Utils::Icon({{":/utils/images/editcopy.png", Utils::Theme::IconsBaseColor}}), AH::tr("Copy"), AH::tr("Copy (Ctrl + C)"), "Ctrl+C", false }, { Utils::Icon({{":/utils/images/editcut.png", Utils::Theme::IconsBaseColor}}), AH::tr("Cut"), AH::tr("Cut (Ctrl + X)"), "Ctrl+X", false }, { Utils::Icon({{":/utils/images/editpaste.png", Utils::Theme::IconsBaseColor}}), AH::tr("Paste"), AH::tr("Paste (Ctrl + V)"), "Ctrl+V", false }, - { Utils::Icon(":/scxmleditor/images/screenshot.png"), AH::tr("Screenshot"), AH::tr("Screenshot (Ctrl + Shift + C)"), "Ctrl+Shift+C", false }, - { Utils::Icon(":/scxmleditor/images/icon-export-canvas.png"), AH::tr("Export to Image"), AH::tr("Export to Image"), "Ctrl+Shift+E", false }, + { Utils::Icons::SNAPSHOT_TOOLBAR, AH::tr("Screenshot"), AH::tr("Screenshot (Ctrl + Shift + C)"), "Ctrl+Shift+C", false }, + { Utils::Icon({{":/scxmleditor/images/icon-export-canvas.png", Utils::Theme::IconsBaseColor}}), AH::tr("Export to Image"), AH::tr("Export to Image"), "Ctrl+Shift+E", false }, { Utils::Icon(":/scxmleditor/images/fullnamespace.png"), AH::tr("Toggle Full Namespace"), AH::tr("Toggle Full Namespace"), "Ctrl+Shift+N", true }, { Utils::Icon(":/scxmleditor/images/align_left.png"), AH::tr("Align Left"), AH::tr("Align Left (Ctrl+L,1)"), "Ctrl+L,1", false }, diff --git a/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp b/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp index 1ad7b9ba2f9..22cbdf4d4e7 100644 --- a/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp +++ b/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp @@ -643,14 +643,6 @@ bool ScxmlDocument::changed() const return !m_undoStack->isClean(); } -void ScxmlDocument::setChanged(bool modified) -{ - if (modified) - ; // we lack a setDirty method in QUndoStack - else - m_undoStack->setClean(); -} - ScxmlTag *ScxmlDocument::scxmlRootTag() const { ScxmlTag *tag = rootTag(); diff --git a/src/plugins/scxmleditor/plugin_interface/scxmldocument.h b/src/plugins/scxmleditor/plugin_interface/scxmldocument.h index 5d654d4d0b1..19506731f23 100644 --- a/src/plugins/scxmleditor/plugin_interface/scxmldocument.h +++ b/src/plugins/scxmleditor/plugin_interface/scxmldocument.h @@ -156,7 +156,6 @@ public: * @return - true if changed, false otherwise */ bool changed() const; - void setChanged(bool modified); /** * @brief rootTag - return rootTag of the document @@ -278,7 +277,6 @@ private: ScxmlTag *createScxmlTag(); QString m_fileName; QUndoStack *m_undoStack; - int m_cleanIndex; QVector m_tags; QHash m_nextIdHash; QHash m_idMap; diff --git a/src/plugins/scxmleditor/scxmleditordocument.cpp b/src/plugins/scxmleditor/scxmleditordocument.cpp index 97569bc7d29..d7c09b9e883 100644 --- a/src/plugins/scxmleditor/scxmleditordocument.cpp +++ b/src/plugins/scxmleditor/scxmleditordocument.cpp @@ -52,12 +52,9 @@ ScxmlEditorDocument::ScxmlEditorDocument(MainWidget *designWidget, QObject *pare // Designer needs UTF-8 regardless of settings. setCodec(QTextCodec::codecForName("UTF-8")); - connect(m_designWidget.data(), &Common::MainWidget::dirtyChanged, this, [this](bool modified){ - setModified(modified); + connect(m_designWidget.data(), &Common::MainWidget::dirtyChanged, this, [this]{ + emit changed(); }); - connect(this, &IDocument::modificationChanged, m_designWidget.data(), &Common::MainWidget::setDirty); - - setModified(m_designWidget->isDirty()); } Core::IDocument::OpenResult ScxmlEditorDocument::open(QString *errorString, const QString &fileName, const QString &realFileName) @@ -132,6 +129,11 @@ MainWidget *ScxmlEditorDocument::designWidget() const return m_designWidget; } +bool ScxmlEditorDocument::isModified() const +{ + return m_designWidget && m_designWidget->isDirty(); +} + bool ScxmlEditorDocument::reload(QString *errorString, ReloadFlag flag, ChangeType type) { if (flag == FlagIgnore) { diff --git a/src/plugins/scxmleditor/scxmleditordocument.h b/src/plugins/scxmleditor/scxmleditordocument.h index a3211c656e9..2ccb98213d5 100644 --- a/src/plugins/scxmleditor/scxmleditordocument.h +++ b/src/plugins/scxmleditor/scxmleditordocument.h @@ -53,6 +53,7 @@ public: bool save(QString *errorString, const QString &fileName, bool autoSave) override; bool shouldAutoSave() const override; bool isSaveAsAllowed() const override; + bool isModified() const override; bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override; // Internal diff --git a/src/plugins/texteditor/images/lightbulb.png b/src/plugins/texteditor/images/lightbulb.png index 3f2e904000d..153d95a9368 100644 Binary files a/src/plugins/texteditor/images/lightbulb.png and b/src/plugins/texteditor/images/lightbulb.png differ diff --git a/src/plugins/texteditor/images/lightbulb@2x.png b/src/plugins/texteditor/images/lightbulb@2x.png index ec43d8f1c1f..24256e2dc9c 100644 Binary files a/src/plugins/texteditor/images/lightbulb@2x.png and b/src/plugins/texteditor/images/lightbulb@2x.png differ diff --git a/src/plugins/texteditor/images/lightbulbcap.png b/src/plugins/texteditor/images/lightbulbcap.png index f860c9c4080..e35b1a9bda0 100644 Binary files a/src/plugins/texteditor/images/lightbulbcap.png and b/src/plugins/texteditor/images/lightbulbcap.png differ diff --git a/src/plugins/texteditor/images/lightbulbcap@2x.png b/src/plugins/texteditor/images/lightbulbcap@2x.png index 83cce03eed5..433822e1324 100644 Binary files a/src/plugins/texteditor/images/lightbulbcap@2x.png and b/src/plugins/texteditor/images/lightbulbcap@2x.png differ diff --git a/src/plugins/texteditor/refactoroverlay.cpp b/src/plugins/texteditor/refactoroverlay.cpp index cc7fd3cebf9..dbce3755981 100644 --- a/src/plugins/texteditor/refactoroverlay.cpp +++ b/src/plugins/texteditor/refactoroverlay.cpp @@ -81,9 +81,10 @@ void RefactorOverlay::paintMarker(const RefactorMarker& marker, QPainter *painte if (icon.isNull()) icon = m_icon; + const qreal devicePixelRatio = painter->device()->devicePixelRatio(); const QSize proposedIconSize = QSize(m_editor->fontMetrics().width(QLatin1Char(' ')) + 3, - cursorRect.height()); - const QSize actualIconSize = icon.actualSize(proposedIconSize); + cursorRect.height()) * devicePixelRatio; + const QSize actualIconSize = icon.actualSize(proposedIconSize) / devicePixelRatio; const int y = cursorRect.top() + ((cursorRect.height() - actualIconSize.height()) / 2); const int x = cursorRect.right(); diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp index 418a0bd11a1..7c1e3157ab8 100644 --- a/src/plugins/texteditor/textdocument.cpp +++ b/src/plugins/texteditor/textdocument.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -106,6 +107,7 @@ public: int m_autoSaveRevision; TextMarks m_marksCache; // Marks not owned + Utils::Guard m_modificationChangedGuard; }; QTextCursor TextDocumentPrivate::indentOrUnindent(const QTextCursor &textCursor, bool doIndent, @@ -236,17 +238,8 @@ void TextDocumentPrivate::updateRevisions() TextDocument::TextDocument(Id id) : d(new TextDocumentPrivate) { - connect(&d->m_document, &QTextDocument::modificationChanged, [this](bool modified) { - // we only want to update the block revisions when going back to the saved version, - // e.g. with undo - if (!modified) - d->updateRevisions(); - setModified(modified); - }); - connect(this, &IDocument::modificationChanged, &d->m_document, &QTextDocument::setModified); - - setModified(d->m_document.isModified()); - + connect(&d->m_document, &QTextDocument::modificationChanged, + this, &TextDocument::modificationChanged); connect(&d->m_document, &QTextDocument::contentsChanged, this, &Core::IDocument::contentsChanged); connect(&d->m_document, &QTextDocument::contentsChange, @@ -598,6 +591,11 @@ bool TextDocument::isFileReadOnly() const return d->m_fileIsReadOnly; } +bool TextDocument::isModified() const +{ + return d->m_document.isModified(); +} + void TextDocument::checkPermissions() { bool previousReadOnly = d->m_fileIsReadOnly; @@ -636,7 +634,11 @@ Core::IDocument::OpenResult TextDocument::openImpl(QString *errorString, const Q readResult = read(realFileName, &content, errorString); const int chunks = content.size(); - d->m_document.setUndoRedoEnabled(reload); + // Don't call setUndoRedoEnabled(true) when reload is true and filenames are different, + // since it will reset the undo's clear index + if (!reload || fileName == realFileName) + d->m_document.setUndoRedoEnabled(reload); + QTextCursor c(&d->m_document); c.beginEditBlock(); if (reload) { @@ -665,7 +667,11 @@ Core::IDocument::OpenResult TextDocument::openImpl(QString *errorString, const Q } c.endEditBlock(); - d->m_document.setUndoRedoEnabled(true); + + // Don't call setUndoRedoEnabled(true) when reload is true and filenames are different, + // since it will reset the undo's clear index + if (!reload || fileName == realFileName) + d->m_document.setUndoRedoEnabled(true); TextDocumentLayout *documentLayout = qobject_cast(d->m_document.documentLayout()); @@ -688,6 +694,11 @@ bool TextDocument::reload(QString *errorString, QTextCodec *codec) } bool TextDocument::reload(QString *errorString) +{ + return reload(errorString, filePath().toString()); +} + +bool TextDocument::reload(QString *errorString, const QString &realFileName) { emit aboutToReload(); TextDocumentLayout *documentLayout = @@ -697,7 +708,7 @@ bool TextDocument::reload(QString *errorString) marks = documentLayout->documentClosing(); // removes text marks non-permanently const QString &file = filePath().toString(); - bool success = openImpl(errorString, file, file, /*reload =*/ true) == OpenResult::Success; + bool success = openImpl(errorString, file, realFileName, /*reload =*/ true) == OpenResult::Success; if (documentLayout) documentLayout->documentReloaded(marks, this); // re-adds text marks @@ -721,8 +732,21 @@ bool TextDocument::setPlainText(const QString &text) bool TextDocument::reload(QString *errorString, ReloadFlag flag, ChangeType type) { - if (flag == FlagIgnore) + if (flag == FlagIgnore) { + if (type != TypeContents) + return true; + + const bool wasModified = document()->isModified(); + { + Utils::GuardLocker locker(d->m_modificationChangedGuard); + // hack to ensure we clean the clear state in QTextDocument + document()->setModified(false); + document()->setModified(true); + } + if (!wasModified) + modificationChanged(true); return true; + } if (type == TypePermissions) { checkPermissions(); return true; @@ -806,6 +830,17 @@ void TextDocument::ensureFinalNewLine(QTextCursor& cursor) } } +void TextDocument::modificationChanged(bool modified) +{ + if (d->m_modificationChangedGuard.isLocked()) + return; + // we only want to update the block revisions when going back to the saved version, + // e.g. with undo + if (!modified) + d->updateRevisions(); + emit changed(); +} + TextMarks TextDocument::marks() const { return d->m_marksCache; diff --git a/src/plugins/texteditor/textdocument.h b/src/plugins/texteditor/textdocument.h index 78cd3dd4b8e..4bb466382ca 100644 --- a/src/plugins/texteditor/textdocument.h +++ b/src/plugins/texteditor/textdocument.h @@ -103,6 +103,7 @@ public: bool setContents(const QByteArray &contents) override; bool shouldAutoSave() const override; bool isFileReadOnly() const override; + bool isModified() const override; bool isSaveAsAllowed() const override; void checkPermissions() override; bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override; @@ -117,6 +118,7 @@ public: OpenResult open(QString *errorString, const QString &fileName, const QString &realFileName) override; virtual bool reload(QString *errorString); + bool reload(QString *errorString, const QString &realFileName); bool setPlainText(const QString &text); QTextDocument *document() const; @@ -150,6 +152,7 @@ private: bool reload); void cleanWhitespace(QTextCursor &cursor, bool cleanIndentation, bool inEntireDocument); void ensureFinalNewLine(QTextCursor &cursor); + void modificationChanged(bool modified); TextDocumentPrivate *d; }; diff --git a/src/plugins/vcsbase/submiteditorfile.cpp b/src/plugins/vcsbase/submiteditorfile.cpp index 9a38537078c..9136118bf7e 100644 --- a/src/plugins/vcsbase/submiteditorfile.cpp +++ b/src/plugins/vcsbase/submiteditorfile.cpp @@ -44,6 +44,7 @@ using namespace Utils; SubmitEditorFile::SubmitEditorFile(const VcsBaseSubmitEditorParameters *parameters, VcsBaseSubmitEditor *parent) : Core::IDocument(parent), + m_modified(false), m_editor(parent) { setId(parameters->id); @@ -82,6 +83,14 @@ bool SubmitEditorFile::setContents(const QByteArray &contents) return m_editor->setFileContents(contents); } +void SubmitEditorFile::setModified(bool modified) +{ + if (m_modified == modified) + return; + m_modified = modified; + emit changed(); +} + bool SubmitEditorFile::save(QString *errorString, const QString &fileName, bool autoSave) { const FileName fName = fileName.isEmpty() ? filePath() : FileName::fromString(fileName); diff --git a/src/plugins/vcsbase/submiteditorfile.h b/src/plugins/vcsbase/submiteditorfile.h index 82263f9ef03..e2da2f72fa8 100644 --- a/src/plugins/vcsbase/submiteditorfile.h +++ b/src/plugins/vcsbase/submiteditorfile.h @@ -45,10 +45,14 @@ public: QByteArray contents() const override; bool setContents(const QByteArray &contents) override; + bool isModified() const override { return m_modified; } bool save(QString *errorString, const QString &fileName, bool autoSave) override; ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override; + void setModified(bool modified = true); + private: + bool m_modified; VcsBaseSubmitEditor *m_editor; }; diff --git a/src/share/3rdparty/generic-highlighter/alert.xml b/src/share/3rdparty/generic-highlighter/alert.xml index 8ac5296fc80..3ed783b35dc 100644 --- a/src/share/3rdparty/generic-highlighter/alert.xml +++ b/src/share/3rdparty/generic-highlighter/alert.xml @@ -30,7 +30,7 @@ Introduce 3 alert levels and sort keywords according importance. Few more keywords has been added. --> - diff --git a/src/share/3rdparty/generic-highlighter/doxygen.xml b/src/share/3rdparty/generic-highlighter/doxygen.xml index 302df7146cc..d07c1650b41 100644 --- a/src/share/3rdparty/generic-highlighter/doxygen.xml +++ b/src/share/3rdparty/generic-highlighter/doxygen.xml @@ -29,7 +29,7 @@ --> - + @@ -297,7 +297,7 @@ - + @@ -339,7 +339,7 @@ - + @@ -405,7 +405,7 @@ - + diff --git a/src/share/3rdparty/generic-highlighter/dtd.xml b/src/share/3rdparty/generic-highlighter/dtd.xml index 75d07234d9b..5c39c1b5970 100644 --- a/src/share/3rdparty/generic-highlighter/dtd.xml +++ b/src/share/3rdparty/generic-highlighter/dtd.xml @@ -3,7 +3,7 @@ ]> - + diff --git a/src/share/3rdparty/generic-highlighter/html.xml b/src/share/3rdparty/generic-highlighter/html.xml index 741423f866e..d1017aad1d3 100644 --- a/src/share/3rdparty/generic-highlighter/html.xml +++ b/src/share/3rdparty/generic-highlighter/html.xml @@ -4,7 +4,7 @@ ]> - + @@ -211,7 +211,7 @@ - + diff --git a/src/share/3rdparty/generic-highlighter/ini.xml b/src/share/3rdparty/generic-highlighter/ini.xml index f6f55e2ebe2..dba0e204cbc 100644 --- a/src/share/3rdparty/generic-highlighter/ini.xml +++ b/src/share/3rdparty/generic-highlighter/ini.xml @@ -1,6 +1,6 @@ - + @@ -34,7 +34,7 @@ - + diff --git a/src/share/3rdparty/generic-highlighter/java.xml b/src/share/3rdparty/generic-highlighter/java.xml index e06ec6bd864..64c97eefd78 100644 --- a/src/share/3rdparty/generic-highlighter/java.xml +++ b/src/share/3rdparty/generic-highlighter/java.xml @@ -1,6 +1,6 @@ - + ACTIVE diff --git a/src/share/3rdparty/generic-highlighter/javadoc.xml b/src/share/3rdparty/generic-highlighter/javadoc.xml index 25eab95e111..c16cba3428b 100644 --- a/src/share/3rdparty/generic-highlighter/javadoc.xml +++ b/src/share/3rdparty/generic-highlighter/javadoc.xml @@ -1,6 +1,6 @@ - + diff --git a/src/share/3rdparty/generic-highlighter/makefile.xml b/src/share/3rdparty/generic-highlighter/makefile.xml index fbf96eae927..9baeb508ecf 100644 --- a/src/share/3rdparty/generic-highlighter/makefile.xml +++ b/src/share/3rdparty/generic-highlighter/makefile.xml @@ -8,7 +8,7 @@ diff --git a/src/share/3rdparty/generic-highlighter/perl.xml b/src/share/3rdparty/generic-highlighter/perl.xml index 2f6e823bfb6..a3c6bdb21b0 100644 --- a/src/share/3rdparty/generic-highlighter/perl.xml +++ b/src/share/3rdparty/generic-highlighter/perl.xml @@ -39,7 +39,7 @@ Enhance tr/// and y/// support. --> - + if diff --git a/src/share/3rdparty/generic-highlighter/ruby.xml b/src/share/3rdparty/generic-highlighter/ruby.xml index c4110ccd754..5c6dca335bc 100644 --- a/src/share/3rdparty/generic-highlighter/ruby.xml +++ b/src/share/3rdparty/generic-highlighter/ruby.xml @@ -31,7 +31,7 @@ - + diff --git a/src/share/3rdparty/generic-highlighter/xml.xml b/src/share/3rdparty/generic-highlighter/xml.xml index 3d92a563cd1..622c2a41e7f 100644 --- a/src/share/3rdparty/generic-highlighter/xml.xml +++ b/src/share/3rdparty/generic-highlighter/xml.xml @@ -6,7 +6,7 @@ ]> - + @@ -134,7 +134,7 @@ - + diff --git a/src/share/3rdparty/generic-highlighter/yacc.xml b/src/share/3rdparty/generic-highlighter/yacc.xml index add68e96a8d..c8b3153218b 100644 --- a/src/share/3rdparty/generic-highlighter/yacc.xml +++ b/src/share/3rdparty/generic-highlighter/yacc.xml @@ -25,7 +25,7 @@ This code is released under the LGPL as part of kdelibs/kate. ======================================================================== --> - + @@ -92,7 +92,7 @@ This code is released under the LGPL as part of kdelibs/kate. - + @@ -112,7 +112,7 @@ This code is released under the LGPL as part of kdelibs/kate. - + diff --git a/src/shared/qbs b/src/shared/qbs index 0971e0b7453..830503d0470 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit 0971e0b7453439f01c72551870d8a2a140758593 +Subproject commit 830503d04708ebd6a64dfd260b35cfeea26ba60a diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp index bea5eacea49..62070b37b0b 100644 --- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp +++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp @@ -50,48 +50,30 @@ #include #include +#include + #include #include namespace ClangBackEnd { -namespace { - -int getIntervalFromEnviromentVariable() -{ - bool isConversionOk = false; - const int intervalAsInt = qEnvironmentVariableIntValue("QTC_CLANG_DELAYED_REPARSE_TIMEOUT", - &isConversionOk); - - if (isConversionOk) - return intervalAsInt; - else - return -1; -} - -int delayedDocumentAnnotationsTimerInterval() -{ - static const int defaultInterval = 1500; - static const int userDefinedInterval = getIntervalFromEnviromentVariable(); - static const int interval = userDefinedInterval >= 0 ? userDefinedInterval : defaultInterval; - - return interval; -} - -} // anonymous - ClangCodeModelServer::ClangCodeModelServer() : documents(projects, unsavedFiles) - , updateDocumentAnnotationsTimeOutInMs(delayedDocumentAnnotationsTimerInterval()) { updateDocumentAnnotationsTimer.setSingleShot(true); - QObject::connect(&updateDocumentAnnotationsTimer, &QTimer::timeout, [this]() { processJobsForDirtyAndVisibleDocuments(); }); + updateVisibleButNotCurrentDocumentsTimer.setSingleShot(true); + QObject::connect(&updateVisibleButNotCurrentDocumentsTimer, + &QTimer::timeout, + [this]() { + processJobsForDirtyAndVisibleButNotCurrentDocuments(); + }); + QObject::connect(documents.clangFileSystemWatcher(), &ClangFileSystemWatcher::fileChanged, [this](const Utf8String &filePath) { @@ -290,16 +272,44 @@ bool ClangCodeModelServer::isTimerRunningForTestOnly() const void ClangCodeModelServer::processJobsForDirtyAndVisibleDocuments() { - for (const auto &document : documents.documents()) { - if (document.isNeedingReparse() && document.isVisibleInEditor()) { - DocumentProcessor processor = documentProcessors().processor(document); - processor.addJob(createJobRequest(document, - JobRequest::Type::UpdateDocumentAnnotations, - PreferredTranslationUnit::PreviouslyParsed)); - } - } + processJobsForDirtyCurrentDocument(); + processTimerForVisibleButNotCurrentDocuments(); +} - documentProcessors().process(); +void ClangCodeModelServer::processJobsForDirtyCurrentDocument() +{ + const auto currentDirtyDocuments = documents.filtered([](const Document &document) { + return document.isNeedingReparse() && document.isUsedByCurrentEditor(); + }); + QTC_CHECK(currentDirtyDocuments.size() <= 1); + + addAndRunUpdateJobs(currentDirtyDocuments); +} + +void ClangCodeModelServer::addAndRunUpdateJobs(const std::vector &documents) +{ + for (const auto &document : documents) { + DocumentProcessor processor = documentProcessors().processor(document); + processor.addJob(createJobRequest(document, + JobRequest::Type::UpdateDocumentAnnotations, + PreferredTranslationUnit::PreviouslyParsed)); + processor.process(); + } +} + +void ClangCodeModelServer::processTimerForVisibleButNotCurrentDocuments() +{ + if (documents.dirtyAndVisibleButNotCurrentDocuments().empty()) { + updateVisibleButNotCurrentDocumentsTimer.stop(); + } else { + updateVisibleButNotCurrentDocumentsTimer.start( + updateVisibleButNotCurrentDocumentsTimeOutInMs); + } +} + +void ClangCodeModelServer::processJobsForDirtyAndVisibleButNotCurrentDocuments() +{ + addAndRunUpdateJobs(documents.dirtyAndVisibleButNotCurrentDocuments()); } void ClangCodeModelServer::processInitialJobsForDocuments(const std::vector &documents) @@ -357,6 +367,11 @@ void ClangCodeModelServer::setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(i updateDocumentAnnotationsTimeOutInMs = value; } +void ClangCodeModelServer::setUpdateVisibleButNotCurrentDocumentsTimeOutInMsForTestsOnly(int value) +{ + updateVisibleButNotCurrentDocumentsTimeOutInMs = value; +} + DocumentProcessors &ClangCodeModelServer::documentProcessors() { if (!documentProcessors_) { diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.h b/src/tools/clangbackend/ipcsource/clangcodemodelserver.h index ab4efdb7e29..b03b4327faf 100644 --- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.h +++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.h @@ -65,16 +65,22 @@ public: // for tests int queueSizeForTestsOnly(); bool isTimerRunningForTestOnly() const; void setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(int value); + void setUpdateVisibleButNotCurrentDocumentsTimeOutInMsForTestsOnly(int value); DocumentProcessors &documentProcessors(); private: - void startDocumentAnnotationsTimerIfFileIsNotOpenAsDocument(const Utf8String &filePath); - void addJobRequestsForDirtyAndVisibleDocuments(); - void processJobsForDirtyAndVisibleDocuments(); + void processInitialJobsForDocuments(const std::vector &documents); void startInitializingSupportiveTranslationUnits(const std::vector &documents); + void processJobsForDirtyAndVisibleDocuments(); + void processJobsForDirtyCurrentDocument(); + void processTimerForVisibleButNotCurrentDocuments(); + void processJobsForDirtyAndVisibleButNotCurrentDocuments(); + + void addAndRunUpdateJobs(const std::vector &documents); + JobRequest createJobRequest(const Document &document, JobRequest::Type type, PreferredTranslationUnit preferredTranslationUnit @@ -88,7 +94,10 @@ private: QScopedPointer documentProcessors_; // Delayed initialization QTimer updateDocumentAnnotationsTimer; - int updateDocumentAnnotationsTimeOutInMs; + int updateDocumentAnnotationsTimeOutInMs = 1500; + + QTimer updateVisibleButNotCurrentDocumentsTimer; + int updateVisibleButNotCurrentDocumentsTimeOutInMs = 2000; }; } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangdocuments.cpp b/src/tools/clangbackend/ipcsource/clangdocuments.cpp index 6077505b573..3c77089447a 100644 --- a/src/tools/clangbackend/ipcsource/clangdocuments.cpp +++ b/src/tools/clangbackend/ipcsource/clangdocuments.cpp @@ -33,6 +33,8 @@ #include #include +#include + #include #include @@ -143,6 +145,20 @@ const std::vector &Documents::documents() const return documents_; } +const std::vector Documents::filtered(const IsMatchingDocument &isMatchingDocument) const +{ + return Utils::filtered(documents_, isMatchingDocument); +} + +std::vector Documents::dirtyAndVisibleButNotCurrentDocuments() const +{ + return filtered([](const Document &document) { + return document.isNeedingReparse() + && document.isVisibleInEditor() + && !document.isUsedByCurrentEditor(); + }); +} + UnsavedFiles Documents::unsavedFiles() const { return unsavedFiles_; diff --git a/src/tools/clangbackend/ipcsource/clangdocuments.h b/src/tools/clangbackend/ipcsource/clangdocuments.h index 90c905f16c4..dc1699178aa 100644 --- a/src/tools/clangbackend/ipcsource/clangdocuments.h +++ b/src/tools/clangbackend/ipcsource/clangdocuments.h @@ -32,6 +32,7 @@ #include +#include #include namespace ClangBackEnd { @@ -57,6 +58,9 @@ public: bool hasDocumentWithFilePath(const Utf8String &filePath) const; const std::vector &documents() const; + using IsMatchingDocument = std::function; + const std::vector filtered(const IsMatchingDocument &isMatchingDocument) const; + std::vector dirtyAndVisibleButNotCurrentDocuments() const; UnsavedFiles unsavedFiles() const; diff --git a/src/tools/clangbackend/ipcsource/clangexceptions.cpp b/src/tools/clangbackend/ipcsource/clangexceptions.cpp index 187ce98d1a1..62527b6ce77 100644 --- a/src/tools/clangbackend/ipcsource/clangexceptions.cpp +++ b/src/tools/clangbackend/ipcsource/clangexceptions.cpp @@ -58,7 +58,7 @@ DocumentDoesNotExistException::DocumentDoesNotExistException(const Utf8String &f + filePath + Utf8StringLiteral("' with the project part id '") + projectPartId - + Utf8StringLiteral("' does not exits!"); + + Utf8StringLiteral("' does not exists!"); } DocumentFileDoesNotExistException::DocumentFileDoesNotExistException( diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg index 4b03178b5ef..7002b7fd402 100644 --- a/src/tools/icons/qtcreatoricons.svg +++ b/src/tools/icons/qtcreatoricons.svg @@ -3383,11 +3383,17 @@ height="11" id="rect6782-96-0-0-7-8-1-0-4-4" /> + sodipodi:nodetypes="ccccccc" /> + @@ -3633,6 +3639,101 @@ inkscape:connector-curvature="0" sodipodi:nodetypes="ccc" /> + + + + + + + + + + + + + + + + + +