Merge remote-tracking branch 'origin/4.2'

Change-Id: Ied7c5b01ade2a71e92541fcced2935adcf143421
This commit is contained in:
Eike Ziller
2016-10-24 13:17:22 +02:00
144 changed files with 2892 additions and 1581 deletions

142
dist/changes-4.2.0.md vendored Normal file
View File

@@ -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:

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -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/}

View File

@@ -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
*/

View File

@@ -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

View File

@@ -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)

View File

@@ -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" \

View File

@@ -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)

View File

@@ -123,6 +123,7 @@ Column {
if (supportGradient && gradientLine.hasGradient) {
colorEditor.color = gradientLine.currentColor
gradientLine.currentColor = color
textField.text = colorEditor.color
}
gradientLine.isInValidState = true
}

View File

@@ -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
}
}

View File

@@ -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()

View File

@@ -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,

View File

@@ -30,6 +30,8 @@
#include <QLocalSocket>
#include <QProcessEnvironment>
#include <QScopedPointer>
#include <QTemporaryDir>
#include <memory>
@@ -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<QProcess> process_;
QLocalSocket localSocket;
QScopedPointer<QTemporaryDir> temporaryDirectory_;
QTimer processAliveTimer;
QString processPath_;
bool isAliveTimerResetted = false;

View File

@@ -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<SourceRangeContainer> ranges_;

View File

@@ -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);

View File

@@ -61,7 +61,6 @@ public:
signals:
void changed();
void modificationChanged(bool modified);
void modelClipboardChanged(bool isEmpty);
void diagramClipboardChanged(bool isEmpty);

View File

@@ -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();
}

View File

@@ -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<Project> m_project;
bool m_isModified = false;
bool m_isModified;
};
} // namespace qmt

View File

@@ -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<std::string> 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 *>(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 */
};

View File

@@ -282,7 +282,7 @@ extern "C" HRESULT CALLBACK pid(CIDebugClient *client, PCSTR args)
int token;
commandTokens<StringList>(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);

113
src/libs/utils/guard.cpp Normal file
View File

@@ -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

56
src/libs/utils/guard.h Normal file
View File

@@ -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 <QtGlobal>
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

View File

@@ -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 <i>%1</i> 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:

View File

@@ -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,

View File

@@ -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 \

View File

@@ -114,6 +114,8 @@ Project {
"flowlayout.cpp",
"flowlayout.h",
"functiontraits.h",
"guard.cpp",
"guard.h",
"historycompleter.cpp",
"historycompleter.h",
"hostosinfo.h",

View File

@@ -159,5 +159,7 @@
<file>images/iconoverlay_warning_background@2x.png</file>
<file>images/bookmark.png</file>
<file>images/bookmark@2x.png</file>
<file>images/snapshot.png</file>
<file>images/snapshot@2x.png</file>
</qresource>
</RCC>

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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:

View File

@@ -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<void (QSpinBox::*)(int)>(&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);
}

View File

@@ -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;

View File

@@ -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());
}

View File

@@ -69,6 +69,7 @@
#include <cplusplus/Icons.h>
#include <QDateTime>
#include <QDir>
#include <QElapsedTimer>
#include <QLoggingCategory>
@@ -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()

View File

@@ -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 \

View File

@@ -28,34 +28,22 @@
#include <coreplugin/editormanager/editormanager.h>
#include <utils/qtcassert.h>
#include <utils/tooltip/tooltip.h>
#include <QApplication>
#include <QDesktopWidget>
#include <QFileInfo>
#include <QHBoxLayout>
#include <QHash>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
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("<b>") + text + QStringLiteral("</b>");
}
QString wrapInLink(const QString &text, const QString &target)
{
return QStringLiteral("<a href='%1' style='text-decoration:none'>%2</a>").arg(target, text);
}
QString wrapInColor(const QString &text, const QByteArray &color)
{
return QStringLiteral("<font color='%2'>%1</font>").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<ClangBackEnd::FixItContainer> &fixits)
void applyFixit(const ClangBackEnd::DiagnosticContainer &diagnostic)
{
ClangCodeModel::ClangFixItOperation operation(Utf8String(), fixits);
ClangCodeModel::ClangFixItOperation operation(Utf8String(), diagnostic.fixIts());
operation.perform();
}
template <typename LayoutType>
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<ClangBackEnd::FixItContainer> 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<ClangBackEnd::DiagnosticContainer> &diagnostics,
const DisplayHints &displayHints)
{
setContentsMargins(0, 0, 0, 0);
auto *mainLayout = createLayout<QVBoxLayout>();
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<QHBoxLayout>();
headerLayout->addWidget(new QLabel(wrapInBoldTags(category)), 1);
QWidget *createWidget(const QVector<ClangBackEnd::DiagnosticContainer> &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<ClangBackEnd::DiagnosticContainer> &diagnostics)
{
// For debugging, add: style='border-width:1px;border-color:black'
QString text = "<table cellspacing='0' cellpadding='0'>";
foreach (const ClangBackEnd::DiagnosticContainer &diagnostic, diagnostics)
text.append(tableRows(diagnostic));
text.append("</table>");
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(
" <tr>"
" <td align='left'><b>%1</b></td>"
" <td align='right'><font color='gray'>%2</font></td>"
" </tr>")
.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 <table>: style='border-width:1px;border-color:red'
const QString text = QString::fromLatin1(
"<table cellspacing='0' cellpadding='0'>"
" <tr>"
" <td>%1: </td>"
" <td width='100%'>%2</td>"
" </tr>"
"</table>")
.arg(clickableLocation(diagnostic, m_mainFilePath),
clickableFixIt(diagnostic, diagnosticText, hasFixit));
return text;
}
QString diagnosticRow(const ClangBackEnd::DiagnosticContainer &diagnostic,
IndentMode indentMode)
{
const QString text = QString::fromLatin1(
" <tr>"
" <td colspan='2' align='left' style='%1'>%2</td>"
" </tr>")
.arg(indentModeToHtmlStyle(indentMode),
diagnosticText(diagnostic));
return text;
}
QString diagnosticRowsForChildren(const ClangBackEnd::DiagnosticContainer &diagnostic)
{
const QVector<ClangBackEnd::DiagnosticContainer> 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<ClangBackEnd::DiagnosticContainer>::const_iterator first,
const QVector<ClangBackEnd::DiagnosticContainer>::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("<a href='%1' style='text-decoration:none'>%2</a>").arg(target, text);
}
static QString ellipsisRow()
{
return QString::fromLatin1(
" <tr>"
" <td colspan='2' align='left' style='%1'>...</td>"
" </tr>")
.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<QString, ClangBackEnd::DiagnosticContainer>;
TargetIdToDiagnosticTable m_targetIdsToDiagnostics;
unsigned m_targetIdCounter = 0;
QString m_mainFilePath;
};
void addChildrenToLayout(const QString &mainFilePath,
const QVector<ClangBackEnd::DiagnosticContainer>::const_iterator first,
const QVector<ClangBackEnd::DiagnosticContainer>::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<ClangBackEnd::DiagnosticContainer> &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<ClangBackEnd::DiagnosticContainer> &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"

View File

@@ -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<ClangBackEnd::DiagnosticContainer> &diagnostics,
const Destination &destination);
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -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<ClangBackEnd::DiagnosticContainer> 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);

View File

@@ -31,6 +31,7 @@
#include <utils/icon.h>
#include <utils/theme/theme.h>
#include <QLayout>
#include <QString>
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;
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -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;

View File

@@ -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<QString, ProjectPart::Ptr> &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<ProjectPart::Ptr> projectParts,
const ExtraToolChainInfo &extraParams)
static AnalyzeUnits unitsToAnalyzeFromProjectParts(const QList<ProjectPart::Ptr> projectParts)
{
qCDebug(LOG) << "Taking arguments for analyzing from ProjectParts.";
@@ -380,9 +377,7 @@ static AnalyzeUnits unitsToAnalyzeFromProjectParts(const QList<ProjectPart::Ptr>
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<QString, ProjectPart::Ptr> projectFileToProjectPart
= generateProjectFileToProjectPartMapping(m_projectInfo.projectParts());
units = unitsToAnalyzeFromCompilerCallData(projectFileToProjectPart,
compilerCallData,
m_extraToolChainInfo);
m_targetTriple);
}
Utils::sort(units, &AnalyzeUnit::file);

View File

@@ -47,11 +47,6 @@ struct AnalyzeUnit {
};
typedef QList<AnalyzeUnit> 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;

View File

@@ -29,6 +29,7 @@
QT_BEGIN_NAMESPACE
class QComboBox;
class QDialog;
class QLabel;
class QPlainTextEdit;
class QPushButton;

View File

@@ -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 \

View File

@@ -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",

View File

@@ -25,12 +25,15 @@
#include "saveitemsdialog.h"
#include <coreplugin/diffservice.h>
#include <coreplugin/fileiconprovider.h>
#include <coreplugin/idocument.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <extensionsystem/pluginmanager.h>
#include <QDir>
#include <QFileInfo>
#include <QPushButton>
@@ -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<Core::DiffService>()) {
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<IDocument*>())
m_filesToDiff.append(doc->filePath().toString());
}
reject();
}
void SaveItemsDialog::discardAll()
{
m_ui.treeWidget->clearSelection();
@@ -156,6 +183,11 @@ QList<IDocument*> SaveItemsDialog::itemsToSave() const
return m_itemsToSave;
}
QStringList SaveItemsDialog::filesToDiff() const
{
return m_filesToDiff;
}
void SaveItemsDialog::setAlwaysSaveMessage(const QString &msg)
{
m_ui.saveBeforeBuildCheckBox->setText(msg);

View File

@@ -54,15 +54,19 @@ public:
void setAlwaysSaveMessage(const QString &msg);
bool alwaysSaveChecked();
QList<IDocument *> itemsToSave() const;
QStringList filesToDiff() const;
private:
void collectItemsToSave();
void collectFilesToDiff();
void discardAll();
void updateSaveButton();
void updateButtons();
void adjustButtonWidths();
Ui::SaveItemsDialog m_ui;
QList<IDocument*> m_itemsToSave;
QStringList m_filesToDiff;
QPushButton *m_diffButton = nullptr;
};
} // namespace Internal

View File

@@ -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 <QObject>
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

View File

@@ -29,6 +29,7 @@
#include "idocument.h"
#include "coreconstants.h"
#include <coreplugin/diffservice.h>
#include <coreplugin/dialogs/readonlyfilesdialog.h>
#include <coreplugin/dialogs/saveitemsdialog.h>
#include <coreplugin/editormanager/editormanager.h>
@@ -38,6 +39,8 @@
#include <coreplugin/editormanager/ieditorfactory.h>
#include <coreplugin/editormanager/iexternaleditor.h>
#include <extensionsystem/pluginmanager.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/mimetypes/mimedatabase.h>
@@ -606,6 +609,11 @@ static bool saveModifiedFilesHelper(const QList<IDocument *> &documents,
(*alwaysSave) = dia.alwaysSaveChecked();
if (failedToSave)
(*failedToSave) = modifiedDocuments;
const QStringList filesToDiff = dia.filesToDiff();
if (!filesToDiff.isEmpty()) {
if (auto diffService = ExtensionSystem::PluginManager::getObject<DiffService>())
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<DiffService>(),
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>())
diffService->diffModifiedFiles(filesToDiff);
}
if (!errorStrings.isEmpty())
QMessageBox::critical(ICore::dialogParent(), tr("File Error"),
errorStrings.join(QLatin1Char('\n')));

View File

@@ -39,6 +39,7 @@
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/dialogs/openwithdialog.h>
#include <coreplugin/dialogs/readonlyfilesdialog.h>
#include <coreplugin/diffservice.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/ieditorfactory.h>
#include <coreplugin/editormanager/iexternaleditor.h>
@@ -1905,9 +1906,9 @@ void EditorManagerPrivate::handleDocumentStateChange()
IDocument *document = qobject_cast<IDocument *>(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<DiffService>();
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))

View File

@@ -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);

View File

@@ -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

View File

@@ -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();

View File

@@ -200,7 +200,9 @@ QString ResourcePreviewHoverHandler::makeTooltip() const
if (mimeType.name().startsWith("image", Qt::CaseInsensitive))
ret += QString("<img src=\"file:///%1\" /><br/>").arg(m_resPath);
ret += QString("<a href=\"file:///%1\">%2</a>").arg(m_resPath).arg(m_resPath);
ret += QString("<a href=\"file:///%1\">%2</a>")
.arg(m_resPath)
.arg(QDir::toNativeSeparators(m_resPath));
}
return ret;
}

View File

@@ -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 &macroName, 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

View File

@@ -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;

View File

@@ -32,6 +32,7 @@ namespace CppTools {
ProjectPart::ProjectPart()
: project(0)
, toolChainWordWidth(WordWidth32Bit)
, isMsvc2015Toolchain(false)
, languageVersion(CXX14)
, languageExtensions(NoExtensions)

View File

@@ -81,6 +81,11 @@ public: // Types
Qt5 = 2
};
enum ToolChainWordWidth {
WordWidth32Bit,
WordWidth64Bit,
};
using Ptr = QSharedPointer<ProjectPart>;
@@ -103,6 +108,7 @@ public: // fields
QByteArray projectDefines;
QByteArray toolchainDefines;
Core::Id toolchainType;
ToolChainWordWidth toolChainWordWidth;
bool isMsvc2015Toolchain;
QString targetTriple;
ProjectPartHeaderPaths headerPaths;

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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 {

View File

@@ -26,6 +26,7 @@
#pragma once
#include <texteditor/textdocument.h>
#include <utils/guard.h>
#include <QPointer>
@@ -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<QDesignerFormWindowInterface> m_formWindow;
bool m_isModified = false;
ResourceHandler *m_resourceHandler = nullptr;
Utils::Guard m_modificationChangedGuard;
};
} // namespace Internal

View File

@@ -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<DiffEditorDocument>(doc))
DiffEditor::DiffEditor(DiffEditorDocument *doc) : DiffEditor()
{
Guard guard(&m_ignoreChanges);
Utils::GuardLocker guard(m_ignoreChanges);
setDocument(QSharedPointer<DiffEditorDocument>(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<FileData> 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());

View File

@@ -29,6 +29,7 @@
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/idocument.h>
#include <utils/guard.h>
QT_BEGIN_NAMESPACE
class QComboBox;
@@ -103,7 +104,7 @@ private:
QPair<QString, QString> m_currentFileChunk;
int m_currentViewIndex;
int m_currentDiffFileIndex;
int m_ignoreChanges;
Utils::Guard m_ignoreChanges;
bool m_sync;
bool m_showDescription;
};

View File

@@ -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) {

View File

@@ -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<FileData> &data, const QString &directory,
const QString &startupFile = QString());

View File

@@ -44,10 +44,14 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
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<Diff> diffList = differ.cleanupSemantics(differ.diff(leftContents, rightContents));
const QList<Diff> diffList = differ.cleanupSemantics(differ.diff(leftContents, rightContents));
QList<Diff> leftDiffList;
QList<Diff> 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<FileData> fileDataList;
TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(
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<Core::IDocument *> openedDocuments =
Core::DocumentModel::openedDocuments();
const QList<IDocument *> openedDocuments =
DocumentModel::openedDocuments();
QList<FileData> fileDataList;
foreach (Core::IDocument *doc, openedDocuments) {
foreach (IDocument *doc, openedDocuments) {
TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(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<FileData> fileDataList;
foreach (const QString fileName, m_fileNames) {
TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(
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<TextEditor::TextDocument *>(
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<DiffEditorDocument *>(
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<TextEditor::TextEditorWidget *>(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<TextEditor::TextDocument *>(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<DiffEditorDocument *>(
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();
}

View File

@@ -27,7 +27,7 @@
#include "diffeditor_global.h"
#include <texteditor/textdocument.h>
#include <coreplugin/diffservice.h>
#include <extensionsystem/iplugin.h>
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<TextEditor::TextDocument> m_currentTextDocument;
QAction *m_diffOpenFilesAction;
};
} // namespace Internal

View File

@@ -32,6 +32,7 @@
#include <coreplugin/patchtool.h>
#include <texteditor/fontsettings.h>
#include <texteditor/textdocument.h>
#include <extensionsystem/pluginmanager.h>
@@ -42,6 +43,7 @@
#include <QDir>
#include <QMenu>
#include <QMessageBox>
#include <QTemporaryFile>
#include <QTextCodec>
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<TextEditor::TextDocument *>(
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,

View File

@@ -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 {

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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()) {

View File

@@ -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);

View File

@@ -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()));

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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");

View File

@@ -86,6 +86,8 @@ public:
void setImportsWidget(QWidget *importsWidget);
static QString qmlSourcesPath();
void clearSearchFilter();
public slots:
void setSearchFilter(const QString &searchFilter);
void delayedUpdateModel();

View File

@@ -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;

View File

@@ -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;

View File

@@ -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; }
};

View File

@@ -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;

View File

@@ -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<TextEditor::TextEditorWidget*>(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<TextEditor::TextEditorWidget*>(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;

View File

@@ -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();

View File

@@ -26,6 +26,7 @@
#include "textmodifier.h"
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <texteditor/convenience.h>
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();

View File

@@ -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<PropertyName, QVariant> &propertyHash)
static inline QHash<PropertyName, QVariant> getProperties(const ModelNode &node)
{
QHash<PropertyName, QVariant> 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<PropertyName, QVar
propertyHash.remove("opacity");
}
}
return propertyHash;
}
static inline void applyProperties(ModelNode &node, const QHash<PropertyName, QVariant> &propertyHash)
@@ -111,14 +113,8 @@ static inline void applyProperties(ModelNode &node, const QHash<PropertyName, QV
static void openFileComponent(const ModelNode &modelNode)
{
QmlDesignerPlugin::instance()->viewManager().nextFileIsCalledInternally();
QHash<PropertyName, QVariant> 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<PropertyName, QVariant> 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<PropertyName, QVariant> 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<PropertyName, QVariant> 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<Core::IEditor *> &editors)
@@ -306,8 +277,9 @@ void DocumentManager::removeEditors(const QList<Core::IEditor *> &editors)
void DocumentManager::goIntoComponent(const ModelNode &modelNode)
{
if (modelNode.isValid() && modelNode.isComponent()) {
if (modelNode.isValid() && modelNode.isComponent() && designDocument()) {
QmlDesignerPlugin::instance()->viewManager().setComponentNode(modelNode);
QHash<PropertyName, QVariant> 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);
}
}

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -15,6 +15,7 @@
<file>images/fullnamespace.png</file>
<file>images/history.png</file>
<file>images/icon-export-canvas.png</file>
<file>images/icon-export-canvas@2x.png</file>
<file>images/icon-fit-screen.png</file>
<file>images/icon-pan.png</file>
<file>images/icon-zoom-in.png</file>
@@ -24,7 +25,6 @@
<file>images/navigator.png</file>
<file>images/parallel.png</file>
<file>images/parallel_icon.png</file>
<file>images/screenshot.png</file>
<file>images/state.png</file>
<file>images/state_color.png</file>
<file>images/statistics.png</file>

Some files were not shown because too many files have changed in this diff Show More