Merge remote-tracking branch 'origin/4.2'
Change-Id: Ied7c5b01ade2a71e92541fcced2935adcf143421
142
dist/changes-4.2.0.md
vendored
Normal 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:
|
||||
BIN
doc/images/qmldesigner-backends.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 3.3 KiB |
@@ -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/}
|
||||
|
||||
@@ -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
|
||||
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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" \
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -123,6 +123,7 @@ Column {
|
||||
if (supportGradient && gradientLine.hasGradient) {
|
||||
colorEditor.color = gradientLine.currentColor
|
||||
gradientLine.currentColor = color
|
||||
textField.text = colorEditor.color
|
||||
}
|
||||
gradientLine.isInValidState = true
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -61,7 +61,6 @@ public:
|
||||
|
||||
signals:
|
||||
void changed();
|
||||
void modificationChanged(bool modified);
|
||||
void modelClipboardChanged(bool isEmpty);
|
||||
void diagramClipboardChanged(bool isEmpty);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
BIN
src/libs/utils/images/snapshot.png
Normal file
|
After Width: | Height: | Size: 148 B |
BIN
src/libs/utils/images/snapshot@2x.png
Normal file
|
After Width: | Height: | Size: 250 B |
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -114,6 +114,8 @@ Project {
|
||||
"flowlayout.cpp",
|
||||
"flowlayout.h",
|
||||
"functiontraits.h",
|
||||
"guard.cpp",
|
||||
"guard.h",
|
||||
"historycompleter.cpp",
|
||||
"historycompleter.h",
|
||||
"hostosinfo.h",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QComboBox;
|
||||
class QDialog;
|
||||
class QLabel;
|
||||
class QPlainTextEdit;
|
||||
class QPushButton;
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
47
src/plugins/coreplugin/diffservice.h
Normal 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
|
||||
@@ -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')));
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -87,6 +87,14 @@ void CompilerOptionsBuilder::addDefine(const QByteArray &defineDirective)
|
||||
m_options.append(defineDirectiveToDefineOption(defineDirective));
|
||||
}
|
||||
|
||||
void CompilerOptionsBuilder::addWordWidth()
|
||||
{
|
||||
const QString argument = m_projectPart.toolChainWordWidth == ProjectPart::WordWidth64Bit
|
||||
? QLatin1String("-m64")
|
||||
: QLatin1String("-m32");
|
||||
add(argument);
|
||||
}
|
||||
|
||||
void CompilerOptionsBuilder::addTargetTriple()
|
||||
{
|
||||
if (!m_projectPart.targetTriple.isEmpty()) {
|
||||
@@ -262,6 +270,20 @@ void CompilerOptionsBuilder::addOptionsForLanguage(bool checkForBorlandExtension
|
||||
m_options.append(opts);
|
||||
}
|
||||
|
||||
void CompilerOptionsBuilder::addDefineToAvoidIncludingGccOrMinGwIntrinsics()
|
||||
{
|
||||
// In gcc headers, lots of built-ins are referenced that clang does not understand.
|
||||
// Therefore, prevent the inclusion of the header that references them. Of course, this
|
||||
// will break if code actually requires stuff from there, but that should be the less common
|
||||
// case.
|
||||
|
||||
const Core::Id type = m_projectPart.toolchainType;
|
||||
if (type == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID
|
||||
|| type == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID) {
|
||||
addDefine("#define _X86INTRIN_H_INCLUDED");
|
||||
}
|
||||
}
|
||||
|
||||
static QByteArray toMsCompatibilityVersionFormat(const QByteArray &mscFullVer)
|
||||
{
|
||||
return mscFullVer.left(2)
|
||||
@@ -342,10 +364,18 @@ void CompilerOptionsBuilder::undefineCppLanguageFeatureMacrosForMsvc2015()
|
||||
// Undefine the language feature macros that are pre-defined in clang-cl 3.8.0,
|
||||
// but not in MSVC2015's cl.exe.
|
||||
foreach (const QString ¯oName, languageFeatureMacros())
|
||||
m_options.append(QLatin1String("/U") + macroName);
|
||||
m_options.append(undefineOption() + macroName);
|
||||
}
|
||||
}
|
||||
|
||||
void CompilerOptionsBuilder::addDefineFloat128ForMingw()
|
||||
{
|
||||
// TODO: Remove once this is fixed in clang >= 3.9.
|
||||
// https://llvm.org/bugs/show_bug.cgi?id=30685
|
||||
if (m_projectPart.toolchainType == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID)
|
||||
addDefine("#define __float128 void");
|
||||
}
|
||||
|
||||
QString CompilerOptionsBuilder::includeOption() const
|
||||
{
|
||||
return QLatin1String("-I");
|
||||
@@ -364,6 +394,11 @@ QString CompilerOptionsBuilder::defineOption() const
|
||||
return QLatin1String("-D");
|
||||
}
|
||||
|
||||
QString CompilerOptionsBuilder::undefineOption() const
|
||||
{
|
||||
return QLatin1String("-U");
|
||||
}
|
||||
|
||||
static bool isGccOrMinGwToolchain(const Core::Id &toolchainType)
|
||||
{
|
||||
return toolchainType == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace CppTools {
|
||||
|
||||
ProjectPart::ProjectPart()
|
||||
: project(0)
|
||||
, toolChainWordWidth(WordWidth32Bit)
|
||||
, isMsvc2015Toolchain(false)
|
||||
, languageVersion(CXX14)
|
||||
, languageExtensions(NoExtensions)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()));
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -86,6 +86,8 @@ public:
|
||||
void setImportsWidget(QWidget *importsWidget);
|
||||
|
||||
static QString qmlSourcesPath();
|
||||
void clearSearchFilter();
|
||||
|
||||
public slots:
|
||||
void setSearchFilter(const QString &searchFilter);
|
||||
void delayedUpdateModel();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||